I am trying to create httpwebrequest to one url (REST API) where i am writing stream to target api server. But before writing stream, in my request object : User Agent is throwing error that 'request.UserAgent' threw an exception of type 'System.NotImplementedException'. Even i have hard coded useragent value also. Same case with other two params AllowAutoRedirect and CookieContainer. On the other hand all other params having correct value or null.
Any help on this, why UserAgent param is throwing this error 'request.UserAgent' threw an exception of type 'System.NotImplementedException'. Below is my web request:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri("TargetAPIIUrl_I am passing here"));
request.Method = "POST";
string boundary = "---------------" + DateTime.Now.Ticks.ToString();
string formDataBoundary = "-----------------------------" + DateTime.Now.Ticks.ToString().Substring(0, 14);
string contentType = "multipart/form-data; boundary=" + formDataBoundary;
request.ContentType = contentType;
request.UserAgent = "Hardcoded string of my target API";
request.BeginGetRequestStream(new AsyncCallback(asyncResult =>
{
Stream stream = request.EndGetRequestStream(asyncResult);
SilverlightApplication1.TubeUtility.DataContractMultiPartSerializer ser = new SilverlightApplication1.TubeUtility.DataContractMultiPartSerializer(boundary);
ser.WriteObject(stream, parameters);
stream.Close();
request.BeginGetResponse(callback, request);
}), request);
The UserAgent and AllowAutoRedirect properties are present to maintain some consistency with the .NET framework HttpWebRequest however neither the ClientHTTP nor the BrowserHTTP implementations support them.
It is possible to use a CookieContainer with the ClientHTTP stack, the BrowserHTTP stack will of course use the host browser cookie management.
Related
So we want to use a webapi that is build in ASP.net and uses OData as protocol. I did some homework and saw that Microsoft has a very good documenten OData Connected Service. The only thing I can't find is that the webapi we want to use has a HMAC for security. I cannot find an example where the OData Connected Service is used with HMAC. Could someone explain if and how HMAC is possible with the OData Connected Service?
Probably the answer depends on the specific implementation of the HMAC on the server side.
If server receives all the data included in the request along with the Authorization header and extracts the values (APP Id, Signature, Nonce and Request Time stamp) from the Authorization header, then client should:
Build a string by combining all the data that will be sent, this string contains the following parameters (APP Id, HTTP method, request URI, request time stamp, nonce, and Base 64 string representation of the request pay load).
The signature will be sent in the Authorization header using a custom scheme such as ”amx”. The data in the Authorization header will contain the APP Id, request time stamp, and nonce separated by colon ‘:’. The format for the Authorization header will be like: [Authorization: amx APPId:Signature:Nonce:Timestamp].
Client send the request as usual along with the generated data in the Authorization header (just use client hooks or httpclient).
Example (after generating client code):
private string APPId = "65d3a4f0-0239-404c-8394-21b94ff50604";
private string APIKey = "WLUEWeL3so2hdHhHM5ZYnvzsOUBzSGH4+T3EgrQ91KI=";
public async Task<IEnumerable<string>> TestODataHMAC()
{
// add there your OData Uri
var container = new DefaultContainer(new Uri("https://services.odata.org/V4/(S(qc322lduoxrqt13nhydbdcvx))/TripPinServiceRW/"));
container.Configurations.RequestPipeline.OnMessageCreating = (args) =>
{
var request = new HttpWebRequestMessage(args);
// Get the Request URI
string requestUri = HttpUtility.UrlEncode(request.Url.AbsoluteUri.ToLower());
// Calculate UNIX time
var epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
var timeSpan = DateTime.UtcNow - epochStart;
var requestTimeStamp = Convert.ToUInt64(timeSpan.TotalSeconds).ToString();
// Create the random nonce for each request
var nonce = Guid.NewGuid().ToString();
// Creating the raw signature string by combinging
// APPId, request Http Method, request Uri, request TimeStamp, nonce
var signatureRawData = string.Format("{0}{1}{2}{3}{4}", APPId, request.Method, requestUri, requestTimeStamp, nonce);
// Converting the APIKey into byte array
var secretKeyByteArray = Convert.FromBase64String(APIKey);
// Converting the signatureRawData into byte array
var signature = Encoding.UTF8.GetBytes(signatureRawData);
// Generate the hmac signature and set it in the Authorization header
using (HMACSHA256 hmac = new HMACSHA256(secretKeyByteArray))
{
var signatureBytes = hmac.ComputeHash(signature);
var requestSignatureBase64String = Convert.ToBase64String(signatureBytes);
//Setting the values in the Authorization header using custom scheme (hmacauth)
request.SetHeader("Authorization", string.Format("hmacauth {0}:{1}:{2}:{3}", APPId, requestSignatureBase64String, nonce, requestTimeStamp));
}
return request;
};
// add there your OData method call
var nquery = container.People.Where(p => p.Gender == PersonGender.Female).Take(10) as DataServiceQuery<Person>;
var response = await nquery?.ExecuteAsync();
return (response as QueryOperationResponse<Person>).Select(p => p.FirstName).ToArray();
}
I am trying to call the Microsoft Graph API to create a domain. Unfortunately when I go to make the call, I receive an error stating that the "JSON Payload is empty".
Here is the call I am making:
GraphServiceClient _graphServiceClient =
new GraphServiceClient(new GraphAuthenticationHelper(NetOrgDomain));
HttpRequestMessage httpRequestMessage =
new HttpRequestMessage(httpMethod, requestUri);
string content = "{\"id\": \"sampleDomainAdd.info\"}";
var json = JsonConvert.SerializeObject(content);
var jsonContent = new StringContent(json, Encoding.UTF8, "application/json");
httpRequestMessage.Content = jsonContent;
HttpResponseMessage response =
await _graphServiceClient.HttpProvider.SendAsync(httpRequestMessage);
You've got an mix of Graph SDK and direct HTTP calls going on here. When using the Microsoft Graph .NET Client Library, you should be using the objects it provides rather than attempting to roll your own.
It also greatly simplifies your code:
var domain = await graphClient.Domains.Request().AddAsync(new Domain
{
Id = "sampleDomainAdd.info"
});
As an aside, the error you're getting currently is due to you're sending the data without the content-type being set to application/json in your HTTP request.
I am trying to authenticate via a service account from Salesforce.com to Google's DFP. I had the integration working under a previous user/credential pair, but am required to update to a new user.
I created the project/user/key pair in the Google Developer Console and added the new service account to the network in DFP. I then changed the "iss" value to be the new user's email and the private key to be the new private key from the keypair.
I am now receiving an 'Invalid Signature' error.
In SFDC, I am using Crypto.sign method with RSA-SHA256.
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_crypto.htm#apex_System_Crypto_sign
I have validated the key format to be PKCS#8 with header and new line characters removed per the documentation (I went so far as to decode the ASN.1 format and inspect the nodes for conformity).
Have I missed a step in the connection between the user and the correct credential? Is there a way for me to validate the signature that I am producing locally to see where I am going wrong? The only difference I have seen is that the old private key was shorter than the current private key.
Below is the code I am using to generate the JWT (again, this code functioned properly with a different username and credential key).
JWTHeader head = new JWTHeader();
head.alg = 'RS256';
head.typ = 'JWT';
JWTClaimSet claim = new JWTClaimSet();
claim.iss = '<username>#*.iam.gserviceaccount.com';
claim.scope = 'https://www.googleapis.com/auth/dfp';
claim.aud = 'https://accounts.google.com/o/oauth2/token';
claim.iat = DateTime.now().getTime() / 1000;
claim.exp = claim.iat + 3600;
System.debug(JSON.serialize(head));
System.debug(JSON.serialize(claim));
String key = '<privatekey>’;
String base = EncodingUtil.urlEncode(EncodingUtil.base64Encode(Blob.valueOf(JSON.serialize(head))), 'UTF-8') + '.' + EncodingUtil.urlEncode(EncodingUtil.base64Encode(Blob.valueOf(JSON.serialize(claim))), 'UTF-8');
String sig = EncodingUtil.urlEncode(EncodingUtil.base64Encode(Crypto.sign('RSA-SHA256', Blob.valueOf(base), EncodingUtil.base64Decode(key))), 'UTF-8');
String body = base + '.' + sig;
System.debug(body);
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint('https://accounts.google.com/o/oauth2/token');
req.setBody('grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=' + body);
req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
req.setMethod('POST');
HttpResponse resp = http.send(req);
Days later I found another solution that solved the issue. The problem was the base64urlsafe encoding. This encoding is not natively done in SFDC and perscribes the removal of trailing padding characters from the base64 string. Luckily, my original username encoded with no padding characters in the claim set. With the new username, the padding characters are present and must be removed before signing.
It all comes down to just a few characters.
I want to authenticate and get a specific course org unit id using valence and java. I have a application id and user key for the application I got from the d2l keytool. I am also using d2l's java client library for authenticating. i.e. com.d2lvalence.idkeyauth.*;
I am getting a http 403 error on the last line of code.
Can someone see what I doing wrong?
URL url = null;
URLConnection connection = null;
String host = "ucbdev.desire2learn.com";
int port = 443;
String appID = "from d2l";
String appKey = "from d2l";
String userId = "";
String userKey = "";
AuthenticationSecurityFactory factory = new AuthenticationSecurityFactory();
// appID and appKey are from d2l
ID2LAppContext appContext = factory.createSecurityContext(appID, appKey);
URI resultUri=new URI("?x_a=fromd2l&x_b=fromd2l");
ID2LUserContext userContext=appContext.createUserContext(resultUri, host, port, true);
if (userContext == null){
System.out.println("USERCONTEXT is NULL");
}
System.out.println("USERCONTEXT HOST NAME IS :"+userContext.getHostName());
userId = userContext.getUserId();
userKey = userContext.getUserKey();
System.out.println("userID is "+userId);
System.out.println("userKey is "+userKey);
URI newUri = userContext.createAuthenticatedUri("/d2l/api/lp/1.0/orgstructure/", "GET");
String res = newUri.toString();
System.out.println("authenticated uri usercontext s "+res);
connection = newUri.toURL().openConnection();
//cast the connection to a HttpURLConnection so we can examin the status code
HttpURLConnection httpConnection = (HttpURLConnection) connection;
httpConnection.setRequestMethod("GET");
StringBuilder sb=new StringBuilder();
BufferedReader in = null;
//if the status code is success then the body is read from the input stream
if(httpConnection.getResponseCode()==200) {
in = new BufferedReader(new InputStreamReader(httpConnection.getInputStream()));
//otherwise the body is read from the output stream
} else {
System.out.println("Error: " + httpConnection.getResponseCode() + ""); //error 403 here
// in = new BufferedReader(new InputStreamReader(httpConnection.getErrorStream()));
}
You do not seem to be clear on how the authentication works for the Valence Learning Framework API.
The AppId/AppKey pair you get back from D2L's KeyTool is the keypair that you'll use to prove that your API call comes from your app (i.e. you pass the AppId in the x_a parameter on a normal call, and you use the AppKey to generate a signature that you then pass in the x_c parameter on the call). But each normal API call also requires user tokens to prove the is being made on behalf of a known user:
All our SDKs work in the same general way:
First you create an application context object that's built using your AppID/Key keypair.
Then, you create an "URL for authentication": this URL will be a call to the special "get user tokens" API call (here the x_a parameter is your AppId, and the x_b parameter is the signature).
You direct the user's browser to go to this URL for authentication, and it's x_target query parameter specifies the callback URL where the LMS should send the user ID/Key pair after it successfully determines who the user is.
Once you have this User ID/Key pair, in subsequent normal API calls, you will pass the User ID in the x_b parameter (as you're passing the App Id in the x_a) and you will use the User Key to make a signature that you will pass in the x_d parameter.
Please follow along the authentication conceptual topic in the docs carefully, as it will show you all the steps involved in the process of your app getting back a UserID/Key pair so you can then use it to make API calls.
I have looked everywhere, but have not found a solution for this issue. I am trying to update a field in SalesForce for a lead. The way I have it sending right now is:
string postData = string.Format("Data I am Sending");
//send data
var data = Encoding.UTF8.GetBytes(postData);
try {
WebRequest request = WebRequest.Create("https://www.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8");
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
Stream newStream = request.GetRequestStream();
newStream.Write(data, 0, data.Length);
newStream.Close();
}
catch { }
instead of it creating a new entry, I want it to update the other fields of the lead where the email address matches the data I send it. So something like:
postData = "oid=myOid&email=" + emailIWantToMatch.Text + "...";
Is this possible or will I have to use the apex api?
The Web2Lead feature can only create new leads, not update existing ones. To do updates you'll need to use either the soap or rest API
Or you can create a Force.com Site Web2Lead page (Creating a Web-to-Lead Form for Your Force.com Site) and have your controller the logic to insert/update based on email-id.