5

I'm trying to attach an api key to the OperationContext outgoing message header as follows:

 public static void AddApikeyToHeader(string apikey, IContextChannel channel, string address) { using (OperationContextScope scope = new OperationContextScope(channel)) { MessageHeader header = MessageHeader.CreateHeader("apikey", address, apikey); OperationContext.Current.OutgoingMessageHeaders.Add(header); } } 

but then I have no idea how to retrieve the header on the server side. I'm using a Service authorisation manager and I get the current operating context and try to retrieve the header like this:

 public string GetApiKey(OperationContext operationContext) { var request = operationContext.RequestContext.RequestMessage; var prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; return prop.Headers["apikey"]; } 

but there is no apikey header attached there. Also, on debugging when I inspect the operationContext I cant seem to see my apikey header anywhere. Can anyone see where I'm going wrong?

3 Answers 3

14
+100

You can add custom header by this way :

using (ChannelFactory<IMyServiceChannel> factory = new ChannelFactory<IMyServiceChannel>(new NetTcpBinding())) { using (IMyServiceChannel proxy = factory.CreateChannel(...)) { using ( OperationContextScope scope = new OperationContextScope(proxy) ) { Guid apiKey = Guid.NewGuid(); MessageHeader<Guid> mhg = new MessageHeader<Guid>(apiKey); MessageHeader untyped = mhg.GetUntypedHeader("apiKey", "ns"); OperationContext.Current.OutgoingMessageHeaders.Add(untyped); proxy.DoOperation(...); } } } 

And service side, you can get header like :

Guid apiKey = OperationContext.Current.IncomingMessageHeaders.GetHeader<Guid>("apiKey", "ns"); 
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks! I figured out the problem i was having was becasue I wasnt making my service call during the lifetime of the context scope!
what is a service channel? IMyServiceChannel?
IMyServiceChannel is communication channel interface between client and server.
Where is the definition of IMyServiceChannel?
1

I'm assuming that you trying to consume your service using some Http Protocol based transport (SOAP, REST etc). I'm also assuming that what you want is to authorize the caller using the supplied API key. If both of those conditions apply to your question, you can read on.

I recently had to tackle a similar problem only that I did not pass an API key but a username/password hash combination using some HTTP custom headers. I ultimately solved it by implementing a custom authorization policy that once configured in Web.config hooked nicely into the WCF Pipeline.

The snippet below should be enough to get you started. You probably would have to replace the x-ms-credentials-XXX headers by a single one representing your API key.

internal class RESTAuthorizationPolicy : IAuthorizationPolicy { public RESTAuthorizationPolicy() { Id = Guid.NewGuid().ToString(); Issuer = ClaimSet.System; } public bool Evaluate(EvaluationContext evaluationContext, ref object state) { const String HttpRequestKey = "httpRequest"; const String UsernameHeaderKey = "x-ms-credentials-username"; const String PasswordHeaderKey = "x-ms-credentials-password"; const String IdentitiesKey = "Identities"; const String PrincipalKey = "Principal"; // Check if the properties of the context has the identities list if (evaluationContext.Properties.Count > 0 || evaluationContext.Properties.ContainsKey(IdentitiesKey) || !OperationContext.Current.IncomingMessageProperties.ContainsKey(HttpRequestKey)) return false; // get http request var httpRequest = (HttpRequestMessageProperty)OperationContext.Current.IncomingMessageProperties[HttpRequestKey]; // extract credentials var username = httpRequest.Headers[UsernameHeaderKey]; var password = httpRequest.Headers[PasswordHeaderKey]; // verify credentials complete if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) return false; // Get or create the identities list if (!evaluationContext.Properties.ContainsKey(IdentitiesKey)) evaluationContext.Properties[IdentitiesKey] = new List<IIdentity>(); var identities = (List<IIdentity>) evaluationContext.Properties[IdentitiesKey]; // lookup user using (var con = ServiceLocator.Current.GetInstance<IDbConnection>()) { using (var userDao = ServiceLocator.Current.GetDao<IUserDao>(con)) { var user = userDao.GetUserByUsernamePassword(username, password); ... 

Comments

0

Did you take a look at this question: How to add a custom HTTP header to every WCF call? ? It may contain your solution.

1 Comment

Yeah I had a look at that, I'd like to try and keep the service authentication manager because its exactly what I need.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.