WCF and authentication - wcf

Yesterday I began learning WCF by porting an existing ASP.NET Web Service.
Creating the WCF service was very easy in itself. Approximately an hour after I created my first WCF Service Library project ever, I was already successfully testing my new WCF service in the WCF Test Client.
Now I would like to implement a simple authentication system, but still do not know how. For the sake of simplicity, say my Web Service has three operations: logging in, getting the length of the user's name, and logging out. How do I complete the TODOs in the following code?
[ServiceContract]
public class MyService
{
[OperationContract(IsInitiating = true, IsTerminating = false)]
public bool Login(string userName, string password)
{
/* I have already implemented the function that validades
whether the user name and password are correct. */
if (ValidateLogin(userName, password))
{
/* TODO: Initiate a session */
return true;
}
else
return false;
}
[OperationContract(IsInitiating = false, IsTerminating = false)]
public int GetUserNameLength()
{
/*
TODO: How to validate whether the user has logged in?
How to obtain the name of the user that has logged in?
*/
int userNameLength = 42;
return userNameLength;
}
[OperationContract(IsInitiating = false, IsTerminating = true)]
public void Logout()
{
/* TODO: How to logout? */
}
}
NOTE: I am the enemy number one of gross hacks. Please lead me towards conceptually "clean" solutions, regardless of their complexity.

The approach you're following may not be correct with WCF. Based on your approach above, the user is already authenticated as it's able to invoke Login operation. Typically, User shouldn't be allowed to invoke any operation until he/she is auhenticated, but in your approach that's not the case.
Also, the sessions in WCF are client initiated, not server initiated. However, based on your approach they seems to be server initiated.
Here're some resources which sheds more light on WCF Security,
http://msdn.microsoft.com/en-us/library/ms731925.aspx
Improve wcf security guidance - http://wcfsecurityguide.codeplex.com/
If you want to use Custom UserNamePassword validator, here is the link,
http://msdn.microsoft.com/en-us/library/aa702565.aspx
HTH,
Amit

It looks like you are trying to handle authentication at the application level. If you have a particular business need to do this then go right ahead but if you just trying to ensure an authenticated user is calling your service then use the build-in WCF authentication mechanisms.
Also, the service contract you are showing is missing this setting in the ServiceContract:
[ServiceContract(SessionMode=SessionMode.Required)]
to make the IsInitiating and IsTerminating actually work. Creating session-based WCF services is pretty limiting because you are forcing all the methods in your service to be occur between he Login ... Logout sequence of calls. If you develop multiple services for your application then trying to orchestrate the interaction with each service in its own session can be very error prone.

Related

How do I use HttpClientFactory with Impersonation? Or find another way to get a JWT token from a service based on a Windows Identity?

I have a regular ASP.Net Core web site that users access using Windows Authentication to determine which users can access which pages.
In order to render a page for the user, the site needs to call in to a series of web services to fetch various bits of data. These web services don't use Windows Authentication. Instead, they require the user's JWT Token.
So, our WebSite needs to exchange the user's Windows token for a JWT token. We have a special ExchangeToken web service that accepts a request using Windows Authentication, and returns the user's JWT Token.
The difficulty comes when I want WebSite to call this ExchangeToken web service. I need to call it using Impersonation, so that I get the user's JWT Token back. However, it doesn't appear to be possible to use HttpClient with Impersonation.
Initially, I had planned to do this in WebSite:
Repeatedly...
Impersonate the user
Instantiate an HttpClient
Call the TokenExchange service to get the JWT Token
Dispose the HttpClient
Stop impersonation
Return the token
However, according to what I've read, re-creating an HTTP client for every call is bad practice, and I should be using HttpClientFactory instead.
However, I don't see how this approach can work with Impersonation.
I tried this:
Use HttpClientFactory to create an HttpClient
Repeatedly...
Impersonate the user
Call the TokenExchange service to get the JWT Token
Stop impersonation
Return the token
However, what happens is that, despite the impersonation, all calls to the TokenExchange service are made with the same windows credentials - the credentials of the user who happens to access the web site first. AFAIK, this stems from the way that Windows Authentication works - it performs a token exchange the first time you use an HttpClient, and from then on, all calls for that client use the same token.
One option would be to create a separate client for each user... but I have about 7,000 users, so that seems a bit excessive!
Another option would be to trust the WebSite to fetch the tokens on behalf of the user, using its own account. The problem with this is that it entails trusting the WebSite. If it is compromised by an attacker, then I can't stop the attacker stealing JWT tokens for arbitrary user. Whereas, with the impersonation, the attacker still can't get a user's JWT token without first obtaining their Windows token.
So, is there a way to do impersonation + IHttpClientFactory together? Or is there a better way to approach all this?
(If it matters, my company has its own Windows servers - we're not in the cloud, yet)
To demonstrate the problem with the second approach, I made a test application. It doesn't actually use HttpClientFactory, but it does demonstrate the problem.
I started with a web site that just returns the user who made a call:
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class WhoController : ControllerBase
{
[HttpGet]
public ActionResult<string> Get()
{
return User.Identity.Name;
}
}
My client code works like this:
private void CallClient(HttpClient httpClient, string username, string password)
{
LogonUser(username, "MYDOMAIN", password, 2, 0, out IntPtr token);
var accessTokenHandle = new SafeAccessTokenHandle(token);
WindowsIdentity.RunImpersonated(
accessTokenHandle,
() =>
{
string result = httpClient.GetStringAsync("http://MyServer/api/who").Result;
Console.WriteLine(result);
});
}
And my test code invokes it like this:
public void Test()
{
var httpClient = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true });
CallClient(httpClient, "User1", "Password1");
CallClient(httpClient, "User2", "Password2");
}
As described above, I get the following written to the console:
User1
User1
What I want is:
User1
User2
TL;DR: NET Core is doing a lot to fight you on this approach under the hood.
Not entirely an answer on what to do, but hopefully helpful background on the HttpClientFactory approach, based on my understanding of the components.
First, from the ASP NET Core docs in regards to impersonation:
ASP.NET Core doesn't implement impersonation. Apps run with the app's
identity for all requests, using app pool or process identity. If the
app should perform an action on behalf of a user, use
WindowsIdentity.RunImpersonated in a terminal inline middleware in
Startup.Configure. Run a single action in this context and then close
the context.
RunImpersonated doesn't support asynchronous operations and shouldn't
be used for complex scenarios. For example, wrapping entire requests
or middleware chains isn't supported or recommended.
As you call out, there's a lot of progress NET Core has made around how HttpClient instances are handled to resolve socket exhaustion and the expensive operations around the underlying handlers. First, there's HttpClientFactory, which in addition to supporting creating named/typed clients with their own pipelines, also attempts to manage and reuse a pool of primary handlers. Second, there's SocketsHttpHandler, which itself manages a connection pool and replaces the previous unmanaged handler by default and is actually used under the hood when you create a new HttpClientHandler. There's a really good post about this on Steve Gordon's Blog: HttpClient Connection Pooling in NET Core. As you're injecting instances of HttpClient around from the factory, it becomes way safer to treat them as scoped and dispose of them because the handlers are no longer your problem.
Unfortunately, all that pooling and async-friendly reuse makes your particular impersonation case difficult, because you actually need the opposite: synchronous calls that clean up after themselves and don't leave the connection open with the previous credentials. Additionally, what used to be a lower-level capability, HttpWebRequest now actually sits on top of HttpClient instead of the other way around, so you can't even skip it all that well by trying to run the requests as a one off. It might be a better option to look into using OpenID Connect and IdentityServer or something to centralize that identity management and Windows auth and pass around JWT everywhere instead.
If you really need to just "make it work", you might try at least adding some protections around the handler and its connection pooling when it comes to the instance that is getting used to make these requests; event if the new clients per request are working most of the time, deliberately cleaning up after them might be safer. Full disclaimer, I have not tested the below code, so consider it conceptual at best.
(Updated Switched the static/semaphore to a regular instance since the last attempt didn't work)
using (var handler = new SocketsHttpHandler() { Credentials = CredentialCache.DefaultCredentials, PooledConnectionLifetime = TimeSpan.Zero, MaxConnectionsPerServer = 1 })
using (var client = new HttpClient(handler, true))
{
return client.GetStringAsync(uri).Result;
}

consuming wcf service secured by adfs in windows phone application

I have a wcf service secured by ADFS deployed in azure. I am able to consume that service in my console application. But when I am not sure how to consume that service in windows phone 7 application.
In my console application, I am retrieving a security token and passing that token to channelfactory object using CreateChannelWithIssuedToken method. But there is no such method in windows phone app to pass the token to wcf service. Can anyone guide me in this issue?
Thanks in advance.
CreateChannelWithIssuedToken was an extension method added by the WIF assembly in .NET 3.5/4.0 (I believe .NET 4.5 has most of this stuff now built-in to the System.ServiceModel namespace). Since you won't have this on the phone, you're stuck with the regular WCF methods to create and use channels.
This is still the case when working on WinForms/WPF apps, though in that case you have the option of bringing in the WIF assembly. Still, it isn't required, and consuming an ADFS-secured service is perfectly doable with the regular WCF classes.
Windows Phone seems to support this stuff, though with some caveats. Looking at the implementation of the extension method, it doesn't seem like they are doing anything all that fancy really:
public static T CreateChannelWithIssuedToken<T>(this ChannelFactory<T> factory, SecurityToken issuedToken)
{
return ChannelFactoryOperations.CreateChannelWithParameters<T>(factory, new FederatedClientCredentialsParameters
{
IssuedSecurityToken = issuedToken
});
}
public static T CreateChannelWithParameters<T>(ChannelFactory<T> factory, FederatedClientCredentialsParameters parameters)
{
ChannelFactoryOperations.VerifyChannelFactory<T>(factory);
T t = factory.CreateChannel();
((IChannel)t).GetProperty<ChannelParameterCollection>().Add(parameters);
return t;
}
The verify method simply performs some diagnostics and throws exceptions (such as if the endpoint isn't set). ChannelParameterCollection is defined in System.ServiceModel.Channels and is supported in Silverlight/WP7. And FederatedClientCredentialsParameters is nothing special either:
public class FederatedClientCredentialsParameters
{
public SecurityToken ActAs ( get; set; )
public SecurityToken OnBehalfOf ( get; set; )
public SecurityToken IssuedSecurityToken ( get; set; )
}
It seems like you should be able to create a channel and use your token with it normally, even from WP7, though I'm afraid I don't have the exact steps to do so. Maybe someone else does or maybe this leads you in the right direction.
This article shows how to access a WIF-protected WCF service from Silverlight, which I imagine is nearly identical to how you'd do it on the phone.
There is a training kit (http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=8396) example ACSAndWindowsPhone7 that may help here (I've not looked at it in detail). I know that Wade Wegner has a number of blog posts on ACS and WP7, but not sure if he's tackled ADFS specifically versus OAuth type mechanisms.

WF4 Services and WIF Integration

Are there proven patterns that anyone could share regarding Workflow 4.0 services integrated with Windows Identity Foundation? We are looking for the best way to inspect the STS token and claims in order to derive who the user is outside the workflow service instance context and make the application's user object available to the workflow context.
We want to maintain separation of concerns between the service implementation of WIF and workflow business logic so our workflow services are highly testable. We have seen a few examples provided which point to wrapping the Receive activity with a code activity that instantiates an implementation of IReceiveMessageCallback in order to get a reference to the OperationContext. Link to Maurice's Blog Post. However, this means activities internal to the service are dependent on the existence of the operation context and possibly even the IClaimsIdentity.
The best solution we can come up with so far is to create an implementation of IDispatchMessageInspector for the service that interrogates the token and creates the application user objects needed by the workflow making them available to the workflow runtime via InstanceContext.Extensions. This seems to work but doesn't feel exactly solid. Any help or feedback is greatly appreciated!
Service Behavior
public class SecurityTokenServiceBehavior : IServiceBehavior, IDispatchMessageInspector
{
...
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
var claimsPrincipal = (IClaimsPrincipal)(Thread.CurrentPrincipal is GenericPrincipal ? null : Thread.CurrentPrincipal);
...
instanceContext.Extensions.Add(new SecurityContextExtension(appUser, audit));
return null;
}
...
}
IReceiveMessageCallback
public class SecurityContextCallback : IReceiveMessageCallback
{
[DataMember]
public SecurityContextExtension SecurityContext { get; private set; }
public void OnReceiveMessage(OperationContext operationContext, ExecutionProperties activityExecutionProperties)
{
SecurityContext = operationContext.InstanceContext.Extensions.Find<SecurityContextExtension>();
}
}
Did you see this blog post about using the ClaimsAuthorizationManager as well? Using the ClaimsAuthorizationManager is the usual way to check for authorization when using WIF.
See Dominick post here for some example on how to embed checks in your code using either the ClaimsAuthorize attribute or the static ClaimsAuthorize.CheckAccess() method. You might also want to take a look at the WF Security Pack CTP 1 here.

How do I do username/password authentication in WCF, with session affinity?

It seems like I'm barking up the wrong tree when asking this question, this question and this question.
I need to authenticate users against a custom API (in COM), and I need to keep that custom API (the COM object) alive (for that user) for future WCF calls. During authentication against that custom API, I can get back a list of custom-defined roles. I'd also like to use these for authorization of the service methods.
Moreover, I need to be able to revoke the user's session remotely. This is triggered by an event raised by the COM API.
I've got a custom UserNamePasswordValidator, but it appears that this has no mechanism for correctly setting a custom principal, so it looks like I'm heading in the wrong direction.
How do I do these three things?
You can handle authentication completely in your service. Create service contract similar to:
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IService
{
// All your operations marked with [OperationContract(IsInitiating=false, IsTerminating=false)]
// Two additional operations
[OperationContract(IsInitiating=true, IsTerminating=false)]
void Login(string user, string password);
[OperationContract(IsInitiating=false, IsTerminating=true)]
void Logout();
}
Service implementing this contract has to have PerSession instancing. Implement authentication in Login method and store COM object in local field. When new client want to use such service he has to first call the Login method. So all your instances will be properly authenticated and they will store their instance of COM object.
You can also register InstanceContext and COM object to some global class which will deal with forcibly killing service instance. This will probably require some research to make it work.
Make sure that you use some secure binding (encryption) because you will send user name and password as a plain text.

spring-security: authorization without authentication

I'm trying to integrate Spring Security in my web application. It seems pretty easy to do as long as you integrate the whole process of authentication and authorization.
However, both authentication and authorization seem so coupled that it's being very time-consuming for me to understand how I could split these processes, and get authentication independently of authorization.
The authentication process is external to our system (based on single sign-on) and this cannot be modified. Nevertheless, once the user succeeds this process, it's loaded in the session, including roles.
What we are trying to achieve is to make use of this information for the authorization process of Spring Security, that's to say, to force it to get the roles from the user session instead of picking it up through the authentication-provider.
Is there any way to achieve this?
If your authentication is already done using an SSO service, then you should use one of spring security's pre-authentication filters. Then you can specify a UserDetails service (possibly custom) that will use the pre-authenticated user principle to populate the GrantedAuthority's
SpringSecurity includes several pre-authentication filters including J2eePreAuthenticatedProcessingFilter and RequestHeaderPreAuthenticatedProcessingFilter. If you can't find one that works for you, its also possible, and not that hard to write your own, provided you know where in the request your SSO implementation stuffs the data. (That depends on the implementation of course.)
Just implement the Filter interface and do something like this in the doFilter method:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// principal is set in here as a header or parameter. you need to find out
// what it's named to extract it
HttpServletRequest req = (HttpServletRequest) request;
if (SecurityContextHolder.getContext().getAuthentication() == null) {
// in here, get your principal, and populate the auth object with
// the right authorities
Authentication auth = doAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
Yes, it's possible. Spring Security (like most of the rest of Spring) is interface-driven so that you can plug in your own implementations selectively for different parts of the framework.
Update: Spring's authorisation and authentication mechanisms work together - the authentication mechanism will authenticate the user and insert various GrantedAuthority instances in the security context. These will then be checked by the authorisation machinery to allow/disallow certain operations.
Use nont's answer for the details on how to use pre-existing authentication. The details of how you get the details from your session (e.g. roles ) will of course depend on your specific setup. But if you put in the GrantedAuthority instances derived from the roles pre-populated in your session by your SSO system, you will be able to use them in your authorisation logic.
From the reference documentation (slightly edited, with my emphasis):
You can (and many users do) write
their own filters or MVC controllers
to provide interoperability with
authentication systems that are not
based on Spring Security. For example,
you might be using Container Managed
Authentication which makes the current
user available from a ThreadLocal or
JNDI location. Or you might work for a
company that has a legacy proprietary
authentication system, which is a
corporate "standard" over which you
have little control. In such
situations it's quite easy to get
Spring Security to work, and still
provide authorization capabilities.
All you need to do is write a filter
(or equivalent) that reads the
third-party user information from a
location, build an Spring
Security-specific Authentication
object, and put it onto the
SecurityContextHolder. It's quite easy
to do this, and it is a
fully-supported integration approach.
The server that handles the authentication should redirect the user to the application passing to it some kind of key (a token in CAS SSO). Then the application use the key to ask to the authentication server the username and roles associated. With this info create a security context that is passed to the authorization manager. This is a very simplified version of a SSO login workflow.
Take a look to CAS SSO and CAS 2 Architecture.
Tell me if you need more information.
we have had the same requirement where we had to use spring security for authorization purpose only. We were using Siteminder for authentication. You can find more details on how to use authorization part of spring security not authentication here at http://codersatwork.wordpress.com/2010/02/13/use-spring-security-for-authorization-only-not-for-authentication/
I have also added source code and test cases at http://code.google.com/p/spring-security-with-authorization-only/source/browse/
I am trying to understand CAS authentication with our own Authorization and was getting confused since the User object in Spring Security always expects the password to be filled in and we don't care about that in our scenario. After reading Surabh's post, it seems that the trick is to return a custom User object without the password filled in. I will try that out and see if it works in my case. Hopefully no other code in the chain will be expecting the Password in the User object.
I use the authorization by this:
Inject the authorization related bean into my own bean:
#Autowired
private AccessDecisionManager accessDecisionManager;
#Autowired
FilterSecurityInterceptor filterSecurityInterceptor;
Use this bean by this:
FilterInvocation fi = new FilterInvocation(rundata.getRequest(), rundata.getResponse(), new FilterChain() {
public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOException, ServletException {
// TODO Auto-generated method stub
}
});
FilterInvocationDefinitionSource objectDefinitionSource = filterSecurityInterceptor.getObjectDefinitionSource();
ConfigAttributeDefinition attr = objectDefinitionSource.getAttributes(fi);
Authentication authenticated = new Authentication() {
...........
public GrantedAuthority[] getAuthorities() {
GrantedAuthority[] result = new GrantedAuthority[1];
result[0] = new GrantedAuthorityImpl("ROLE_USER");
return result;
}
};
accessDecisionManager.decide(authenticated, fi, attr);
I too did spent lot of hours investigating on how to implement custom authorization without authentication.
The authentication process is external to our system (based on single sign-on).
I have done it, as mentioned below and it Works!!! (I am sure there are many other ways to it better, but this way just suits my scenario well enough)
Scenario : User is already authenticated by external system and all information needed for authorization is present in the request
1.
Security config need to be created, enabling global method security as below.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
class SpringWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
}
}
2.) Implement Spring PermissionEvaluator to authorize whether the request should be allowed or rejected
#Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
public boolean authorize(final String groups, final String role) {
boolean allowed = false;
System.out.println("Authorizing: " + groups + "...");
if (groups.contains(role)) {
allowed = true;
System.out.println(" authorized!");
}
return allowed;
};
#Override
public boolean hasPermission(final Authentication authentication, final Object groups, final Object role) {
return authorize((String) groups, (String) role);
};
#Override
public boolean hasPermission(final Authentication authentication, final Serializable targetId, final String targetType, final Object permission) {
return authorize((String) targetId, (String) permission);
};
}
3.) Add MethodSecurityConfig
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
return expressionHandler;
}
}
4.) Add #PreAuthorize in your controller as shown below. In this example, all the groups of the user are present in request header with key 'availableUserGroups'.
This is then passed on to the CustomPermissionEvaluator to verify authorization. Please note that spring automatically passes Authentication object to the method 'hasPermission'.
So in case if you want to load user and check using spring 'hasRole' method, then this can be used.
#PreAuthorize("hasPermission(#userGroups, 'ADMIN')")
#RequestMapping(value = "/getSomething")
public String getSomething(#RequestHeader(name = "availableUserGroups") final String userGroups) {
return "resource allowed to access";
}
Handling Other Scenarios :
1.) In scenario where you want to load the user before you can perform the authorization. You can use spring pre-authentication filters, and do it in a similar way.
Example link : http://www.learningthegoodstuff.com/2014/12/spring-security-pre-authentication-and.html