WCF service consuming passively issued SAML token - wcf

What is the best way to pass an existing SAML token from a website already authenticated via a passive STS?
We have built an Identity Provider which is issuing passive claims to the website for authentication. We have this working. Now we would like to add some WCF services into the mix - calling them from the context of the already authenticated web application. Ideally we would just like to pass the SAML token on without doing anything to it (i.e. adding new claims / re-signing). All of the examples I have seen require the ActAs sts implementation - but is this really necessary? This seems a bit bloated for what we want to achieve.
I would have thought a simple implementation passing the bootstrap token into the channel - using the CreateChannelActingAs or CreateChannelWithIssuedToken mechanism (and setting ChannelFactory.Credentials.SupportInteractive = false) to call the WCF service with the correct binding (what would that be?) would have been enough.
We are using the Fabrikam example code as reference, but as I say, think the ActAs functionality here is overkill for what we are trying to achieve.

What you need in this case is to insert the contents of your token into each outgoing message. If you look at the WIF Identity Training Toolkit they have an IssuedTokenHeader class that will facilitate this (along with the ClaimsIdentitySessionManager). These classes were built for Silverlight but, it doesn't change the solution they offer.
Here is an excerpt from the ClaimsIdentitySessionManager class.
using (OperationContextScope scope = new OperationContextScope(contextChannel))
{
IssuedTokenHeader header = new IssuedTokenHeader(this.TokenCache.GetTokenFromCache(serviceAppliesTo));
OperationContext.Current.OutgoingMessageHeaders.Add(header);
asyncOperation();
}

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;
}

Getting a token for Web API

Once upon a time I used to install Thinktecture.IdentityModel, call it using basic auth to get a token and then pass this in the headers of ajax calls to Web API.
I just tried to do this, and it barfed.
Up periscope!
Method not found: no match for ctor signature (hurrah for Fiddler).
Comparison with a project in which it does work reveals that the required signature exists in version 2.0.0.0 of System.IdentityModel.Tokens.Jwt but is no longer present in version 4.0.20622.1351
Quick, Robin - to the Bingpole!
There's a github support query on this very topic and in the comments we find
leastprivilege commented on Jan 27
The AuthenticationHandler is not the recommended approach anymore for
Web API v2 since everything is now built-in - use middleware instead.
Unask the question, Grasshopper
This is a definitive opinion. He's one of the library's authors. It is also incredibly unhelpful to anyone who needs to ask the question.
Could someone could point me at appropriate introductory and tutorial links so I can join the ranks of those who nod sagely and marvel at the zen-like brevity of this comment?
I was using AuthenticationHandler to validate out of SQL Server table containing a username and password. Purists please don't lecture me, there are countless businesses that don't or won't use third party OAUTH. I need to convert a username/password pair into a session token that I can use on my Web API methods. That's all. I happen to agree about OAUTH but the people paying for this are not interested in grand unified authentication, they like silos.
Supplementary info
I found a publically accessible PluralSight course on the whole authentication thing. It's by the fellow who wrote the ThinkTecture.AuthenticationHandler and it's quite good as a backgrounder on recent change.
In the course of the material he refers to another PluralSight course on MVC, which has more information on OWIN (which is pure API ie an interface) and Katana (which is Microsoft's implementation of OWIN).
At this point it is clear to me that
There has been a fundamental architecture change for assorted good reasons.
A MessageHandler is not the place to do authentication.
There is a NuGet package called Thinktecture.IdentityModel.Owin.BasicAuthentication which is described as OWIN middleware for HTTP Basic Authentication in OWIN/Katana applications.
My still fuzzy understanding of the new landscape suggests that the middleware package is what I need. NuGet did its thing and now I have to provide some code to insert the middleware into the OWIN middleware chain and more code to actually validate the credentials. As near as I can tell I need to do this in Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseBasicAuthentication("some_realm", (id, secret) =>
{
if (id == secret) //should check database, but never mind right now
{
var claims = new List<Claim> {
new Claim(ClaimTypes.NameIdentifier, id),
new Claim(ClaimTypes.Role, "Foo") //this can come from db also
};
return Task.FromResult<IEnumerable<Claim>>(claims);
}
return Task.FromResult<IEnumerable<Claim>>(null);
});
Assuming I've got this right so far, how do I use it? Do I continue to decorate Web API methods with [Authorize]?
Success! Nearly.
At the moment a request from my test client causes a hit on the breakpoint I've set on the line if (id == secret) and the condition is satisfied because id and secret contain the values sent by my test code. Claims objects are created as per the code above and duly returned, but the test client receives 401 Unauthorized.
This is almost certainly due to a failure to create a principal object. There's some business with a UserManager and an IdentityModel, and it may be possible to customise that to use our own schema.

Enabling OAuth1 Support on a Jersey Jax-rs web service

I'd like to enable OAuth1 Provider support on my restful web service. Jersey supports this as described here Jersey OAuth1 Provider support.
I've been trying to register it as so:
public ApplicationConfig(){
super();
addRestResourceClasses(getMyResourceClasses());
register(new OAuth1ServerFeature(new DefaultOAuth1Provider(),"/oauth/access_token","/oauth/request_token"));
}
But, when I register the OAuth1ServerFeature, I get a 404 when trying to access my resources.
Can't seem to find any examples/tutorials implementing jersey oauth support anywhere!
Is there a simple component I can plug into my jax-rs service to enable oauth support?
I realise this thread is somewhat old - but having just got it work myself, I felt a reply was in order! Given time, I may even create a blog post with a fuller example. Be warned - this is not a short answer!
There is an absolute lack of examples on information on using the OAuth1 server (aka Provider) feature in Jersey - I can't remember a tech topic that revealed so little useful Google information. I almost passed on looking for another solution since it led me to think perhaps it didn't work. But, with some perseverance, I can say that not only is it usable, but it seems to work rather well. Plus of course, if you're already using Jersey for your REST API - you don't need any extra libs.
I am not an OAuth1 expert - and I'd strongly recommend some background reading for those attempting this. I am also assuming here you have Jersey working, understand things like ContainerRequestFilters, and also have some internal means to authorize users.
My examples also use the excellent JAX-RS OSGi connector - the only real difference is that where we use an OSGi bundle context to register the OAuth1 feature via an OSGI service, regular Jersey users will need to configure via their normal Application / Server config model.
Initialisation
You must create your OAuth1 feature - and give it a provider:
DefaultOAuth1Provider oap = new DefaultOAuth1Provider();
Feature oaFeature = new OAuth1ServerFeature(oap, "oauth1/request_token", "oauth1/access_token");
Don't forget to register oaFeature into Jersey!
The DefaultOAuth1Provider is entirely memory based - which was fine for us to start with. Many will want to persist access tokens for use across server restarts, which will require an extended subclass (or clean implementation)
Add in your Consumers Keys and Secrets
It took me a while to realise Consumers were not users but clients i.e. applications. The Jersey implementation will not work if you don't register keys and secrets for each consumer (aka client app) that wishes to connect
oap.registerConsumer("some-owner-id",
"abcdef" ,
"123456",
new MultivaluedHashMap<String,String> ());
You obviously would never hard-code these, and further would use some form of secure store for the secret (param 3).
If you do not add these you will not get any further.
OAuth protocol step 1 - get a request token
At this stage you are ready client side to get a request token - and here there is a perfectly good example on GitHub.
ConsumerCredentials consumerCredentials = new ConsumerCredentials("abcdef","123456");
//TODO - user proper client builder with real location + any ssl context
OAuth1AuthorizationFlow authFlow = OAuth1ClientSupport.builder(consumerCredentials)
.authorizationFlow(
"http://myhost:8080/myapi/oauth1/request_token",
"http://myhost:8080/myapi/oauth1/access_token",
"http://myhost:8080/myapi/oauth1/authorize")
.build();
String authorizationUri = authFlow.start();
System.out.println("Auth URI: " + authorizationUri);
Obviously you would change URLs to point to your server and - crucially - the client needs to use the same Conumer Key and Secret you registered in the server.
You will get back a response with an oauth_token string in it e.g.
http://myhost:8080/myapi/oauth/authorize?oauth_token=a1ec37598da
b47f6b9d770b1b23a5f99
OAuth protocol step 2 - authorize the user
As you will read in any article, actual user Authorization is outside of the scope of OAuth1 - at this stage you must invoke your servers auth process whatever that is.
However!!!! What is not outside the OAuth1 scope is what your server needs to do if the user authorizes successfully. You must tell your DefaultOAuth1Provider about the successful auth:
// Dummy code - make out like we're auth'd
Set<String> dummyRoles = new HashSet<> (Arrays.asList( new String[] { "my-role-1", "my-role-2" }));
DefaultOAuth1Provider.Token tok1 = getRequestToken("a1ec37598da
b47f6b9d770b1b23a5f99");
String verifier = authorizeToken(tok1, new Principal()
{
public String getName()
{
return "my-user";
}
},
dummyRoles);
System.out.println("***** verifier: " + verifier);
Note the request token string is that from step 1. Obviously a real implementation would pass a real Principal and set of roles for the authorized user.
Also, of course, printing out the verifier is not much use - you need to get that back to your client in some way, either via an independent channel or possibly as a header in the auth response - which maybe would need to be encrypted for added protection.
OAuth protocol step 3 - swap the request token for an access token
Once the client receives or has the verifier entered manually, it can finalize the process and swap the request token for an access token e.g.
String verifier = System.console().readLine("%s", "Verifier: ");
final AccessToken accessToken = authFlow.finish(verifier);
System.out.println("Access token: " + accessToken.getToken());
Again, not a realistic example - but it shows the process.
If your OAuth1Provider saves access tokens to some persistent store on the server, you can re-use any access token returned here on a future session without going through all the previous steps.
That's it - you then just need to make sure every request the client creates from this point on in the process makes use of that access token.

Accessing user name in IDispatchMessageInspector

I've implemented custom logging logic for WCF service by using IDispatchMessageInspector.
I'm logging entire SOAP request/response in the database by utilizing both AfterReceiveRequest and BeforeSendReply.
I'm using claims-based authentication which works without any issues.
However, when I attempt to access Thread.CurrentPrincipal.Identity.Name or ClaimsPrincipal.Current.Identity.Name, I get empty string always (identity is not set, thus name is blank).
Is there a way to access the identity in any way from IDispatchMessageInspector?
Thank you!
If your claim-based authentication is working, you could add the user to your claimset.
You can access your ClaimSet in a static way:
ReadOnlyCollection<ClaimSet> claimSets = ServiceSecurityContext.Current.AuthorizationContext;`
The other possibility is adding a ServiceAuthorizationManager to your service, register it in your configuration and access your ClaimSet from this class.
Hope this helps

WCF RESTful API

I created a WCF project with one simple method that returns a pdf in a byte[] and a int (id #) and has username+password with a custom validator for message security and a SSL for transport security. Now the client tells me that he was assuming I was going to create a RESTful API instead. I don't have any experience with REST, but I've seen that you can create a REST project in WCF (which is what I'd prefer for interaction with the rest of my solution).
First, can you deliver a pdf the same way in a RESTful API? I set the int as an out parameter in order to return it to the client, can I assume an out parameter will function the same as well?
Second, can I use the same kind of security setup? I'm assuming the SSL will still protect the transport, but I cannot seem to find a good example or tutorial on basic security. I can use a different method of security if needed.
REST is different than SOAP or even WCF in that you aren't using cumbersome "envelopes" unfortunately those envelopes provide you with functionality like the authentication scheme you're using (and out params, etc.)
See Best Practices for securing a REST API / web service
You can go /w basic authentication + SSL for authentication. You must encrypt basic authentication though or else it is insecure. As for the out parameter, it seems that a composite XML based result like the following is one way to go:
GET
could return XML like:
<result>
<intValue>4</intValue>
<pdfByteString><![CDATA[bytestring...etc.]]></pdfByteString>
</result>
It actually does look like WCF does have some built-in functionality to help you out, this white paper should provide a decent intro:
http://msdn.microsoft.com/en-us/library/ee391967.aspx#Y1720