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?
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");
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);
...
Did you take a look at this question: How to add a custom HTTP header to every WCF call? ? It may contain your solution.
Related
I've tried searching for my problems but nothing seems to ask what I wanted to ask.
I'm working on a web service that generates and sends a kind of token from server to client, currently I'm using Glav CacheAdapter (the web cache kind)
When someone requested a data call, the server should generate a token then saves it to a cache, then sends the key to the client, the client then should send the same token to the server and it should be checked with the one in the cache, but somehow when the server generates the key it successfully creates and saves one (I tested when debugging), but when the client call sends the token (it is the same one) but somehow the cache does not contain any data.
>>>> Project A
>> Service
public string Generate()
{
AppServices.Cache.InnerCache.Add($"AuthenticationTokenCache:{xxx}", DateTime.Now.AddDays(1), new StringValue() { Value = xxx });
return key;
}
public bool Validate(string token)
{
return AppServices.Cache.InnerCache.Get<StringValue>($"AuthenticationTokenCache:{xxx}") != null;
}
>> WebAPI
public bool CallValidate(string token)
{
var xService = new Service();
return xService.Validate(token);
}
>>>> Project B
>> WebAPI
protected override bool RequestValidation(string token)
{
var client = new HttpClient();
var authURL = $"/api/CallValidate?token={token}";
var response = client.GetAsync(authURL).Result.Content;
string jsonContent = response.ReadAsStringAsync().Result;
var authResult = JsonConvert.DeserializeObject<bool>(jsonContent);
if(authResult)
{
return true;
}
}
Is the cache type I use wrong, or maybe there's something wrong that I don't realize is wrong?
And when I create new instance of the same class does the cache gets shared between those object or not?
I'm not really sure as to how the details of how caching works, any pointer as to reference reading material would be helpful too.
Thank you.
I have a BasicHttpBinding WCF service. I want to get user name and password in request header. I searched in in the internet for this but I see just WSHttpBinding. I want to have something like this:
//WCF client call
WCFTestService.ServiceClient myService = new
WCFTestService.ServiceClient();
myService.ClientCredentials.UserName.UserName = "username";
myService.ClientCredentials.UserName.Password = "p#ssw0rd";
MessageBox.Show(myService.GetData(123));
myService.Close();
but I don't know what should I write for server side?
Thanks
You could create a custom Authorization Class by inheriting the ServiceAuthorizationManager class and pull out the credentials from the request header.
Your code could be similar to the following:
public class CustomAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
//Extract the Authorization header, and parse out the credentials converting the Base64 string:
var authHeader = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
if ((authHeader != null) && (authHeader != string.Empty))
{
var svcCredentials = System.Text.Encoding.ASCII
.GetString(Convert.FromBase64String(authHeader.Substring(6)))
.Split(':');
var user = new
{
Name = svcCredentials[0],
Password = svcCredentials[1]
};
if ((user.Name == "username" && user.Password == "p#ssw0rd"))
{
//User is authorized and originating call will proceed
return true;
}
else
{
//not authorized
return false;
}
}
else
{
//No authorization header was provided, so challenge the client to provide before proceeding:
WebOperationContext.Current.OutgoingResponse.Headers.Add("WWW-Authenticate: Basic realm=\"YourNameSpace\"");
//Throw an exception with the associated HTTP status code equivalent to HTTP status 401
throw new WebFaultException(HttpStatusCode.Unauthorized);
}
}
}
In addition to that, you need to set the serviceAuthorizationManagerType attribute of the serviceAuthorization element to your custom class in the web.config file.
Something similar to this:
<serviceAuthorization serviceAuthorizationManagerType="YourNameSpace.CustomAuthorizationManager, YourAssemblyName"/>
In the client side, you also need to add the credentials to the request headers.
HttpRequestMessageProperty httpReqProp = new HttpRequestMessageProperty();
httpReqProp.Headers[HttpRequestHeader.Authorization] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("username"+ ":" + "p#ssw0rd"));
Security note:
Keep in mind that in Basic Authentication, the username and password will be sent as non-encrypted text in the request header. You should only implement this with SSL.
We are trying to communicate with a REST server, which uses its own OAuth2 implementation.
This server is written by another company in Java, so I don't have much influence about it.
I've got all the necessary information, like Access Token URL, Refresh URL, Client Id, Client Secret, etc. I can already request an access token and then request some other data from this server, using the REST client Postman.
Now I'd like to use the ServiceStack client (version 4.5.14), to communicate with this server in C# .NET 4.6.2.
My problem is: All the examples I found, e.g. http://docs.servicestack.net/authentication-and-authorization#custom-authentication-and-authorization are either about the server-side or about authentication against Facebook or Google.
I already implemented my own CustomOAuth2Provider, setting the access token URL, ConsumerSecret, etc.
But how do I tell the JsonServiceClient, to use this Provider, before executing the specific request?
Thank you,
Daniel
Edit:
I read a lot of documentation and ServiceStack sourcecode, and I think my main problems are the following:
I abuse the ServiceStack Client to communicate with a non-ServiceStack application, which I can not modify.
Maybe the OAuth2 implementation of the third-party application is not 100% correct, as it expects authorization and token request in the same request.
But I got it working now and would like to show my solution here.
It still can be improved, e.g. it does not use the received refresh token right now.
public class ThirdPartyAuthenticator : IDisposable
{
// TODO: Move to config
public const string AccessTokenUrl = "";
public const string ConsumerKey = "";
public const string ConsumerSecret = "";
public const string Username = "";
public const string Password = "";
/// <summary>
/// Remember the last response, instance comprehensive so we do not need a new token for every request
/// </summary>
public static ServiceModel.ThirdPartyOAuth2Response LastOAuthResponse = null;
/// <summary>
/// This already authenticated client can be used for the data requests.
/// </summary>
public JsonServiceClient AuthenticatedServiceClient { get; set; }
public ThirdPartyAuthenticator()
{
if (LastOAuthResponse == null || (LastOAuthResponse.ExpiryDateTime < DateTime.Now)) // TODO: Use Refresh token?
{
// Get token first
JsonServiceClient authClient = new JsonServiceClient(AccessTokenUrl);
authClient.UserName = ConsumerKey;
authClient.Password = ConsumerSecret;
authClient.AlwaysSendBasicAuthHeader = true;
var request = new ServiceModel.ThirdPartyOAuth2Request();
request.Username = Username;
request.Password = Password;
// Use the Get URI, because server expects username + password as query parameter
LastOAuthResponse = authClient.Post<ServiceModel.ThirdPartyOAuth2Response>(request.ToGetUrl(), request);
}
// If no exception was thrown, we have a valid token here.
AuthenticatedServiceClient = new JsonServiceClient(AccessTokenUrl);
AuthenticatedServiceClient.BearerToken = LastOAuthResponse.AccessToken;
}
public void Dispose()
{
AuthenticatedServiceClient?.Dispose();
}
}
usage:
using (var foo = new ThirdPartyAuthenticator())
{
var response = foo.AuthenticatedServiceClient.Get(new ServiceModel.GetMyData() { SomeId = 10 });
}
OAuth providers require a browser to redirect to the OAuth provider site where Users are able to accept authentication with the App and any permissions it requires. Once the user accepts they're redirected back to your ServiceStack App where it will create an Authenticated User Session. The session id from the Authenticated User Session is what's configured on the ServiceStack client to establish authenticated requests.
Here are some Example Apps which use OAuth to Authenticate using a browser then capture the browser redirect to extract the session cookies and configure it on the C# Service Client where they're then able to make Authenticated requests:
https://github.com/ServiceStackApps/TechStacksAuth
https://github.com/ServiceStackApps/AndroidJavaChat
I'm attempting to consume a 3rd party WSDL. I have added it as a service reference. I initalize the client and query paramaters like this:
var ltRequest = new SearchEmailAddressStatus
{
EmailAddress = emailAddressList.ToArray()
};
var ltClient = new CommunicationPreferenceServiceClient
{
ClientCredentials =
{
UserName =
{
UserName = ltProperties.CompanyCredential.UserName,
Password = ltProperties.CompanyCredential.Password
}
}
};
var ltResponse = ltClient.searchEmailAddressStatusWS(ltRequest);
After watching the packets in Fiddler, I've noticed the Auth header is never sent to the server. Is there any way to manually insert an authorization header in my request?
Okay, after a lot of digging I found the answer. After declaring the client, I used the following code:
using (var scope = new OperationContextScope(ltClient.InnerChannel))
{
var reqProperty = new HttpRequestMessageProperty();
reqProperty.Headers[HttpRequestHeader.Authorization] = "Basic "
+ Convert.ToBase64String(Encoding.ASCII.GetBytes(
ltClient.ClientCredentials.UserName.UserName + ":" +
ltClient.ClientCredentials.UserName.Password));
OperationContext.Current
.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = reqProperty;
var ltResponse = ltClient.searchEmailAddressStatusWS(ltRequest);
}
I believe this is the least-dirty means of getting a customized header inside wsdl request. If someone has a better method, I'd love to hear it.
I am creating an app to get information from Fitbit.com using OAuth.
protected void btnConnect_Click(object sender, EventArgs e)
{
// Create OAuthService object, containing oauth consumer configuration
OAuthService service = OAuthService.Create(
new EndPoint(RequestTokenUrl, "POST"), // requestTokenEndPoint
new Uri(AuthorizationUrl), // authorizationUri
new EndPoint(AccessTokenUrl, "POST"), // accessTokenEndPoint
true, // useAuthorizationHeader
"http://app.fitbit.com", // realm
"HMAC-SHA1", // signatureMethod
"1.0", // oauthVersion
new OAuthConsumer(ConsumerKey, ConsumerSecret) // consumer
);
try
{
var personRepository = new PersonRepository();
var person = personRepository.GetPersonById(int.Parse(personSelect.SelectedItem.Value));
OAuthRequest request = OAuthRequest.Create(
new EndPoint(ProfileUrl, "GET"),
service,
this.Context.Request.Url,
//this.Context.Session.SessionID);
person.FitbitAuthAccessToken,
);
request.VerificationHandler = AspNetOAuthRequest.HandleVerification;
OAuthResponse response = request.GetResource();
// Check if OAuthResponse object has protected resource
if (!response.HasProtectedResource)
{
var token = new OAuthToken(TokenType.Request, person.FitbitAuthAccessToken,
person.FitbitAuthSecret, ConsumerKey);
// If not we are not authorized yet, build authorization URL and redirect to it
string authorizationUrl = service.BuildAuthorizationUrl(response.Token).AbsoluteUri;
Response.Redirect(authorizationUrl);
}
person.FitbitAuthAccessToken = response.Token.Token;
person.FitbitAuthSecret = response.Token.Secret;
person.PersonEncodedId = Doc["result"]["user"]["encodedId"].InnerText;
personRepository.Update(person);
// Store the access token in session variable
Session["access_token"] = response.Token;
}
catch (WebException ex)
{
Response.Write(ex.Message);
Response.Close();
}
catch (OAuthRequestException ex)
{
Response.Write(ex.Message);
Response.Close();
}
}
I save Fitbit Access Token and Secret in database.
How can I get information using just Access token and secret, without authorizing every time?
This would assume that the FitBit api was robust enough to not quire authentication every single time. I have seen API's implementing OAuth where you have an authentication process, then from there most of your calls simply require the AccessToken or secret. I would look at the method signatures for the service and see what types of parameters they are requiring.
If you look at the FitBit API about authentication and accessing resources, you will see that you just need to request the data you are interested in and add in the oAuth header with the access token. Here is what it should look like (from the API page):
GET /1/user/-/activities/date/2010-04-02.json HTTP/1.1
Host: api.fitbit.com
Authorization: OAuth realm="api.fitbit.com",
oauth_consumer_key="fitbit-example-client-application",
oauth_token="8d3221fb072f31b5ef1b3bcfc5d8a27a",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1270248088",
oauth_nonce="515379974",
oauth_signature="Gf5NUq1Pvg3DrtxHJyVaMXq4Foo%3D"
oauth_version="1.0"`
The base signature string will look like:
GET&http%3A%2F%2Fapi.fitbit.com%2F1%2Fuser%2F-%2Factivities%2Fdate%2F2010-04-02.json&oauth_consumer_key%3Dfitbit-example-client-application%26oauth_nonce%3D515379974%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270248088%26oauth_token%3D8d3221fb072f31b5ef1b3bcfc5d8a27a%26oauth_version%3D1.0
I figured I'd offer my VerifyAuthenticationCore that is part of my FitbitClient that inherits from OAuthClient. It took me a while to get this working but I found that I was missing HttpDeliveryMethods.AuthorizationHeaderRequest when I was creating the web request. Adding this allowed the call to stop returning bad request (400) error messages.
The code below is basically using the user id and the access token to get the user profile information. All calls should basically work this way. All you would need to do is change the url and provide the id and token.
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
string username;
var accessToken = response.AccessToken;
var userId = response.ExtraData["encoded_user_id"];
var httpWebRequest = WebWorker.PrepareAuthorizedRequest(new MessageReceivingEndpoint(new Uri("http://api.fitbit.com/1/user/" + userId + "/profile.json"), HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest), accessToken);
var dictionary = new Dictionary<string, string>();
dictionary.Add("accesstoken", accessToken);
dictionary.Add("link", "http://www.fitbit.com/user/" + userId);
using (var webResponse = httpWebRequest.GetResponse())
{
using (var stream = webResponse.GetResponseStream())
using (var reader = new StreamReader(stream))
{
var profile = JObject.Parse(reader.ReadToEnd())["user"];
dictionary.AddItemIfNotEmpty("name", profile["displayName"]);
dictionary.AddItemIfNotEmpty("pictureUrl", profile["avatar"]);
username = dictionary["name"];
}
}
return new AuthenticationResult(true, ProviderName, userId, username, dictionary);
}