I am using the JsonServiceClient client to talk to a RESTful service hosted on IIS 7 by an external vendor.
Here is some sample code I am using to call their Get method.
ServiceStack.ServiceClient.Web.JsonServiceClient client = new ServiceStack.ServiceClient.Web.JsonServiceClient("UrlToVendor"));
client.SetCredentials("userName", "password");
client.AlwaysSendBasicAuthHeader = true;
DTOReturn result = client.Get<DTOReturn>(string.Empty);
I always get an authorization failure. We put a sniffer and the Authorization header is being sent as:
basic userName:password
instead of
Basic userName:password
We were able to use standard .Net calls to get it to work
System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(
"UrlToVendor");
string authInfo = "userName:password";
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
req.Accept = "application/json"; //text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
req.PreAuthenticate = true;
req.Method = "GET";
req.Headers["Authorization"] = string.Format("Basic {0}", authInfo);
System.Net.HttpWebResponse res = (System.Net.HttpWebResponse)req.GetResponse();
And these standard calls failed the same as the JasonServiceClient if we changed "Basic" to "basic".
Any suggestions?
Looks like someone had the same problem. This recent commit changed the auth-scheme from "basic" to "Basic". https://github.com/ServiceStack/ServiceStack/commit/d4f21c5355ab87d7315e142372eef9a40e096b5f
You should be able to just update your dlls.
According to RFC 2617 sec 1.2 the auth-scheme is case-insensitive.
See https://www.rfc-editor.org/rfc/rfc1945#page-47. I would be curious as to why the vendor service won't accept it.
Related
So I am currently working on making SOAP API request to a service with WCF generated code "Client object", I am wondering how to set the Cookie header to the request?
In general, we add the custom HTTP header by using HttpRequestMessageProperty. Please refer to the below code.
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
try
{
using (OperationContextScope ocs=new OperationContextScope(client.InnerChannel))
{
var requestProp = new HttpRequestMessageProperty();
requestProp.Headers["myhttpheader"] = "Boom";
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestProp;
var result = client.SayHelloAsync();
Console.WriteLine(result.Result);
}
Result.
WebOperationContext is a convenience wrapper around the OperationContext. At present, it hasn’t been implemented yet in the Aspnet Core.
https://github.com/dotnet/wcf/issues/2686
Feel free to let me know if there is anything I can help with.
I am trying to authenticate RESTful service (sabre REST api) using RESTsharp library but i am not able to authenticate it. I am using my Client id and secret. Please tell me how to authenticate using oAuth 2.0 authenticator.
I have tried this code. ( sabre is using OAuth 2.0 authentication )
public ActionResult Index()
{
var client = new RestClient("https://api.test.sabre.com");
client.Authenticator = new HttpBasicAuthenticator("myclientid", "myclientsecret");
RestRequest request = new RestRequest("/v1/auth/token", Method.POST);
request.AddHeader("Authorization", "Basic " + client);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("grant_type", "client_credentials");
IRestResponse response = client.Execute(request);
var content = response.Content;
ViewBag.R = content;
return View();
}
i got this result
{"error":"invalid_client","error_description":"Credentials are missing or the syntax is not correct"}
please tell what i am doing wrong.
Thanks
Snapshot of Fiddler Comparison of Running code (not with RestSharp) and code using RestSharp is shown
With RestSharp
Seems to me like you are adding the Authorization header twice. The documentation here says
The authenticator’s Authenticate method is the very first thing called
upon calling RestClient.Execute
Looking at the implementation of HttpBasicAuthenticator, the Authenticate method adds the appropriate header to the request.
So remove the following line from your example:
request.AddHeader("Authorization", "Basic " + client);
You need to first obtain access token from Sabre that you can later use while making rest api calls.
The access token POST request looks like this:
POST https://api.test.sabre.com/v2/auth/token
Authorization: Basic ZVc5MWNtTnNhV1Z1ZEdsazplVzkxY21Oc2FXVnVkSE5sWTNKbGRBPT0=
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
where the value of Authorization after Basic is the Base64 encoded string based on your clientId and secret
Refer to Sabre Authentication on how this string is created
So, in order to get the access token you just need to send a POST request with required header and request parameters and you do not need to use the Authenticator
I am trying to consume SharePoint 2013 REST services from a Desktop application ( cross-platform, cross-os ). Application is basically a HTML page in application view.
Is there a simple way I can authenticate my calls using HTTP methods ?
Yes, you can get authenticated and receive a digest via a REST call.
string url = "http://Your.SP.Site";
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
string cmd = "_api/contextinfo";
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("ContentType", "application/json");
client.DefaultRequestHeaders.Add("ContentLength", "0");
StringContent httpContent = new StringContent("");
var response = client.PostAsync(cmd, httpContent).Result;
if (response.IsSuccessStatusCode)
{
string content = response.Content.ReadAsStringAsync().Result;
JsonObject val = JsonValue.Parse(content).GetObject();
JsonObject d = val.GetNamedObject("d");
JsonObject wi = d.GetNamedObject("GetContextWebInformation");
retVal = wi.GetNamedString("FormDigestValue");
}
The above example shows how to retrieve the digest in C# with the HttpClient. This string needs to be passed as a header to all of the other rest calls you make to carry forward the authentication. You can create a credential by passing in a username and password if needed.
I have more examples here:
https://arcandotnet.wordpress.com/2015/04/01/sharepoint-2013-rest-services-using-c-and-the-httpclient-for-windows-store-apps/
You can do these calls in JavaScript as well and Microsoft has a lot of documentation on that. There is also .NET library, Microsoft.SharePoint.Client.DLL (CSOM) that simplifies this type of coding but you must have the library installed on the client.
We're trying to send Google Authentication tokens to our Rest interface on Google App Engine.
As explained here (under heading "Calling a Google API")
https://developers.google.com/accounts/docs/OAuth2InstalledApp
We should do this by including an Authorization: Bearer HTTP header.
On the client side we're using;
public ClientResource getClientResource(Reference pReference, String pAccessToken)
{
ClientResource lClientResource = new ClientResource(pReference);
ChallengeResponse lChallengeResponse = new ChallengeResponse(ChallengeScheme.HTTP_OAUTH_BEARER);
lChallengeResponse.setRawValue(pAccessToken);
lClientResource.setChallengeResponse(lChallengeResponse);
return lClientResource;
}
So the ChallengeResponse with setRawValue() is used to set the header;
ChallengeResponse lChallengeResponse = new ChallengeResponse(ChallengeScheme.HTTP_OAUTH_BEARER);
lChallengeResponse.setRawValue(pAccessToken);
lClientResource.setChallengeResponse(lChallengeResponse);
For our J2SE clients this is working. Although there's an error message;
"Challenge scheme HTTP_Bearer not supported by the Restlet engine."
we're able to read the token on the server side with;
ChallengeResponse lChallengeResponse = pRequest.getChallengeResponse();
String lAccessToken = lChallengeResponse.getRawValue();
While this is working for our J2SE clients, our GWT clients always return lChallengeResponse == null
Is this the correct method to set the Authorization: Bearer HTTP header or should we add extra/other configuration options?
Why isn't this working for our GWT clients (return null)?
How do we get rid of the error message on our J2Se clients?
I would like to consume a CXF web-service from a .net c# client. We are currently working with java-to-java requests and we protect SOAP envelopes through ws-security (WSS4J library).
My question is: how can I implement a C# WS-client which produces the same SOAP requests as the following client-side java code?
//doc is the original SOAP envelope to process with WSS4J
WSSecHeader secHeader = new WSSecHeader();
secHeader.insertSecurityHeader(doc);
//add username token with password digest
WSSecUsernameToken usrNameTok = new WSSecUsernameToken();
usrNameTok.setPasswordType(WSConstants.PASSWORD_DIGEST);
usrNameTok.setUserInfo("guest",psw_guest);
usrNameTok.prepare(doc);
usrNameTok.appendToHeader(secHeader);
//sign the envelope body with client key
WSSecSignature sign = new WSSecSignature();
sign.setUserInfo("clientx509v1", psw_clientx509v1);
sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
Document signedDoc = null;
sign.prepare(doc, sigCrypto, secHeader);
signedDoc = sign.build(doc, sigCrypto, secHeader);
//encrypt envelope body with server public key
WSSecEncrypt encrypt = new WSSecEncrypt();
encrypt.setUserInfo("serverx509v1");
// build the encrypted SOAP part
String out = null;
Document encryptedDoc = encrypt.build(signedDoc, encCrypto, secHeader);
return encryptedDoc;
Does anybody know where I could find a microsoft how-to or a .net working example?
================================ EDIT ====================================
Thank you Ladislav! I applied your suggestions and I came up with something like:
X509Certificate2 client_pk, server_cert;
client_pk = new X509Certificate2(#"C:\x509\clientKey.pem", "blablabla");
server_cert = new X509Certificate2(#"C:\x509\server-cert.pfx", "blablabla");
// Create the binding.
System.ServiceModel.WSHttpBinding myBinding = new WSHttpBinding();
myBinding.TextEncoding = ASCIIEncoding.UTF8;
myBinding.MessageEncoding = WSMessageEncoding.Text;
myBinding.Security.Mode = SecurityMode.Message;
myBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
myBinding.Security.Message.AlgorithmSuite =
System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128;
// Disable credential negotiation and the establishment of
// a security context.
myBinding.Security.Message.NegotiateServiceCredential = false;
myBinding.Security.Message.EstablishSecurityContext = false;
// Create the endpoint address.
EndpointAddress ea =
new EndpointAddress(new Uri("http://bla.bla.bla"),
EndpointIdentity.CreateDnsIdentity("issuer"));
// configure the username credentials on the channel factory
UsernameClientCredentials credentials = new UsernameClientCredentials(new
UsernameInfo("superadmin", "secret"));
// Create the client.
PersistenceClient client = new PersistenceClient(myBinding, ea);
client.Endpoint.Contract.ProtectionLevel =
System.Net.Security.ProtectionLevel.EncryptAndSign;
// replace ClientCredentials with UsernameClientCredentials
client.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
client.Endpoint.Behaviors.Add(credentials);
// Specify a certificate to use for authenticating the client.
client.ClientCredentials.ClientCertificate.Certificate = client_pk;
// Specify a default certificate for the service.
client.ClientCredentials.ServiceCertificate.DefaultCertificate = server_cert;
// Begin using the client.
client.Open();
clientProxyNetwork[] response = client.GetAllNetwork();
As a result I get (server-side) the following CXF exception:
java.security.SignatureException: Signature does not match.
at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:421)
at sun.security.provider.certpath.BasicChecker.verifySignature(BasicChecker.java:133)
at sun.security.provider.certpath.BasicChecker.check(BasicChecker.java:112)
at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate (PKIXMasterCertPathValidator.java:117)
Therefore it seems a key jks->pem conversion problem... Or am I am missing something in the client-code above?
Well, in the end the solution is to encrypt and sign the whole username token. As for the interoperability, the ws addressing must be activated in cxf and a custom binding in c# is needed. The custom binding that did the trick is basically
AsymmetricSecurityBindingElement abe =
(AsymmetricSecurityBindingElement)SecurityBindingElement.
CreateMutualCertificateBindingElement(MessageSecurityVersion.
WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);
Wcf signs each ws addressing element, therefore the same must be done server side.
This is usually pretty big problem because WCF does not support UserNameToken Profile with Digested password. I needed it few months ago and we had to implement our own custom binding but that code is not ready for publishing. Fortunatelly this blog article describes other implementation and contains sample code with new UserNameClientCredentials class supporting digested password.
Btw. same security configuration should be possible with older API called WSE 3.0. It was replaced by WCF but still some WS-* stack configuration are much simpler with that API and old ASMX services.