I am having some problems attempting to post to an API gateway endpoint.
On my API gateway I have my gateway all set up, and tested via the tool and am getting results and can verify that the step function is in fact executing the request appropriately.
{
"executionArn": "arn:aws:states:us-east-2:xxxxxxxxxxxx:execution:DevStateMachine-XXXXXXXXXXX:c9047982-e7f8-4b72-98d3-281db0eb4c30",
"startDate": 1531170720.489
}
I have set up a Stage for this for my dev environment and all looks good there as well. where I am given a URL to post against.
https://xxxxxxxxxx.execute-api.us-east-2.amazonaws.com/dev/assignments
In my c# code I have the web client defined as follows:
public Guid QueueAssignment(AssignmentDTO assignment)
{
using (var client = new HttpClient())
{
var data = JsonConvert.SerializeObject(assignment);
var content = new StringContent(data);
var uri = "https://xxxxxxxxxx.execute-api.us-east-2.amazonaws.com/dev/assignments"
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = client.PostAsync(uri, content).Result;
if (response.IsSuccessStatusCode)
{
_logger.Info("Successfully posted to AWS Step Function");
_logger.Info(response);
}
else
_logger.Error("Error posting to AWS Step Function");
_logger.Error(response);
}
}
Everytime this post is attempted I get the following error:
System.Net.WebException: The remote name could not be resolved: 'https://xxxxxxxxxx.execute-api.us-east-2.amazonaws.com'
Is there something I am missing in posting to this URI or some type of conversion I need to do? Im kind of at a loss on where to go on this on.
Related
I want to call a SignalR event from Postman, I have gone through this question as well, But it's not helping me.
It's giving the below error, I have mentioned the URL as myUrl due to security reasons in this example.
Below is the C# equivalent code for the same.
private HubConnection hub connection;
private IHubProxy hubProxy;
hubConnection = new HubConnection(myUrl, useDefaultUrl: false);
hubConnection.TransportConnectTimeout = new TimeSpan(10000);
hubConnection.Credentials = CredentialCache.DefaultCredentials;
hubConnection.Closed += _hubConnection_Closed;
hubConnection.Error += _hubConnection_Error;
hubProxy = hubConnection.CreateHubProxy(myHubName);
hubProxy.On<CurrencyPair>("getItems", value =>
{
DoSomething(value);
}
Postman version 9.31.9
Working on a project which integrates Google Cloud's speech-to-text api in an android and iOS environment. Ran through the example code provided (https://cloud.google.com/speech-to-text/docs/samples) and was able to get it to run. Used them as a template to add voice into my app, however there is a serious danger in the samples, specifically in generating the AccessToken (Android snippet below):
// ***** WARNING *****
// In this sample, we load the credential from a JSON file stored in a raw resource
// folder of this client app. You should never do this in your app. Instead, store
// the file in your server and obtain an access token from there.
// *******************
final InputStream stream = getResources().openRawResource(R.raw.credential);
try {
final GoogleCredentials credentials = GoogleCredentials.fromStream(stream)
.createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
This was fine to develop and test locally, but as the comment indicates, it isn't safe to save the credential file into a production app build. So what I need to do is replace this code with a request from a server endpoint. Additionally i need to write the endpoint that will take the request and pass back a token. Although I found some very interesting tutorials related to Firebase Admin libraries generating tokens, I couldn't find anything related to doing a similar operation for GCP apis.
Any suggestions/documentation/examples that could point me in the right direction are appreciated!
Note: The server endpoint will be a Node.js environment.
Sorry for the delay, I was able to get it all to work together and am now only circling back to post an extremely simplified how-to. To start, I installed the following library on the server endpoint project https://www.npmjs.com/package/google-auth-library
The server endpoint in this case is lacking any authentication/authorization etc for simplicity's sake. I'll leave that part up to you. We are also going to pretend this endpoint is reachable from https://www.example.com/token
The expectation being, calling https://www.example.com/token will result in a response with a string token, a number for expires, and some extra info about how the token was generated:
ie:
{"token":"sometoken", "expires":1234567, "info": {... additional stuff}}
Also for this example I used a ServiceAccountKey file which will be stored on the server,
The suggested route is to set up a server environment variable and use https://cloud.google.com/docs/authentication/production#finding_credentials_automatically however this is for the examples sake, and is easy enough for a quick test. These files look something like the following: ( honor system don't steal my private key )
ServiceAccountKey.json
{
"type": "service_account",
"project_id": "project-id",
"private_key_id": "378329234klnfgdjknfdgh9fgd98fgduiph",
"private_key": "-----BEGIN PRIVATE KEY-----\nThisIsTotallyARealPrivateKeyPleaseDontStealIt=\n-----END PRIVATE KEY-----\n",
"client_email": "project-id#appspot.gserviceaccount.com",
"client_id": "12345678901234567890",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/project-id%40appspot.gserviceaccount.com"
}
So here it is a simple endpoint that spits out an AccessToken and a number indicating when the token expires (so you can call for a new one later).
endpoint.js
const express = require("express");
const auth = require("google-auth-library");
const serviceAccount = require("./ServiceAccountKey.json");
const googleauthoptions = {
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
credentials: serviceAccount
};
const app = express();
const port = 3000;
const auth = new auth.GoogleAuth(googleauthoptions);
auth.getClient().then(client => {
app.get('/token', (req, res) => {
client
.getAccessToken()
.then((clientresponse) => {
if (clientresponse.token) {
return clientresponse.token;
}
return Promise.reject('unable to generate an access token.');
})
.then((token) => {
return client.getTokenInfo(token).then(info => {
const expires = info.expiry_date;
return res.status(200).send({ token, expires, info });
});
})
.catch((reason) => {
console.log('error: ' + reason);
res.status(500).send({ error: reason });
});
});
app.listen(port, () => {
console.log(`Server is listening on https://www.example.com:${port}`);
});
return;
});
Almost done now, will use android as an example. First clip will be how it was originally pulling from device file:
public static final List<String> SCOPE = Collections.singletonList("https://www.googleapis.com/auth/cloud-platform");
final GoogleCredentials credentials = GoogleCredentials.fromStream(this.mContext.getResources().openRawResource(R.raw.credential)).createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
final string token = accesstoken.getTokenValue();
final long expires = accesstoken.getExpirationTime().getTime()
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
prefs.edit().putString(PREF_ACCESS_TOKEN_VALUE, value).putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, expires).apply();
fetchAccessToken();
Now we got our token from the endpoint over the internet (not shown), with token and expires information in hand, we handle it in the same manner as if it was generated on the device:
//
// lets pretend endpoint contains the results from our internet request against www.example.com/token
final string token = endpoint.token;
final long expires = endpoint.expires
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
prefs.edit().putString(PREF_ACCESS_TOKEN_VALUE, value).putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, expires).apply();
fetchAccessToken();
Anyway hopefully that is helpful if anyone has a similar need.
===== re: AlwaysLearning comment section =====
Compared to the original file credential based solution:
https://github.com/GoogleCloudPlatform/android-docs-samples/blob/master/speech/Speech/app/src/main/java/com/google/cloud/android/speech/SpeechService.java
In my specific case I am interacting with a secured api endpoint that is unrelated to google via the react-native environment ( which sits on-top of android and uses javascript ).
I already have a mechanism to securely communicate with the api endpoint I created.
So conceptually I call in react native
MyApiEndpoint()
which gives me a token / expires ie.
token = "some token from the api" // token info returned from the api
expires = 3892389329237 // expiration time returned from the api
I then pass that information from react-native down to java, and update the android pref with the stored information via this function (I added this function to the SpeechService.java file)
public void setToken(String value, long expires) {
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
prefs.edit().putString(PREF_ACCESS_TOKEN_VALUE, value).putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, expires).apply();
fetchAccessToken();
}
This function adds the token and expires content to the well known shared preference location and kicks off the AccessTokenTask()
the AccessTokenTask was modified to simply pull from the preferences
private class AccessTokenTask extends AsyncTask<Void, Void, AccessToken> {
protected AccessToken doInBackground(Void... voids) {
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String tokenValue = prefs.getString(PREF_ACCESS_TOKEN_VALUE, null);
long expirationTime = prefs.getLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, -1);
if (tokenValue != null && expirationTime != -1) {
return new AccessToken(tokenValue, new Date(expirationTime));
}
return null;
}
You may notice I don't do much with the expires information here, I do the checking for expiration elsewhere.
Here you have a couple of useful links:
Importing the Google Cloud Storage Client library in Node.js
Cloud Storage authentication
I am trying to do an APEX Trigger Callout using POST to Google Cloud. The problem I have is: whenever the trigger happened, I see the authentication is happening fine, but message is coming as "Undefined" only from Stackdriver logs, hence the cloud function will fail since the message should be in JSON format. Not sure why it is happening. I am pretty much new to Salesforce. Please provide some hints on this.
If I test the cloud function directly (Using Testing option inside cloud function), I see the insert to BigQuery table as expected, but from Salesforce trigger it seems, the message is not being captured properly. PFB Codes used in Salesforce.Expecting the same message which we are inserting into LEAD table in Salesforce as Rest API request
Salesforce Class:
Public class Callout {
#future(callout=true)
Public static void httpcallout(){
Lead c = [Select Name from Lead Limit 1] ;
system.debug('Halo Trigger');
JSONGenerator gen = JSON.createGenerator(true);
gen.writeStartObject();
gen.writeStringField('Name', c.Name);
gen.writeEndObject();
String jsonS = gen.getAsString();
System.debug('jsonMaterials'+jsonS);
String endpoint = 'https://us-central1-valid-weaver- 235212.cloudfunctions.net/Salesforce-GCP';
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setbody(jsonS);
Http http = new Http();
HTTPResponse response = http.send(req);
}
}
Salesforce Trigger
trigger SFGCP on Lead (after insert) {
callout.httpcallout();
system.debug('Hello');
}
Debug from Salesforce
Lead c = new Lead(Company ='Test',LastName='Admin');
insert c;
Below code works fine with some minor change in Cloud Function . Thanks
Public class Callout {
#future(callout=true)
Public static void httpcallout()
{
Lead c = [select Name, LeadSource, Company from Lead order by createdDate DESC limit 1] ;
system.debug('Halo Trigger');
JSONGenerator gen = JSON.createGenerator(true);
gen.writeStartObject();
gen.writeStringField('Name', c.Name);
gen.writeStringField('LeadSource', c.LeadSource);
gen.writeStringField('Company', c.Company);
gen.writeEndObject();
String jsonS = gen.getAsString();
System.debug('jsonMaterials'+jsonS);
String endpoint = 'https://us-central1-valid-weaver-235212.cloudfunctions.net /Salesforce-GCP';
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setbody(jsonS);
Http http = new Http();
HTTPResponse response = http.send(req);
}
}
Identity server is implemented and working well. Google login is working and is returning several claims including email.
Facebook login is working, and my app is live and requests email permissions when a new user logs in.
The problem is that I can't get the email back from the oauth endpoint and I can't seem to find the access_token to manually request user information. All I have is a "code" returned from the facebook login endpoint.
Here's the IdentityServer setup.
var fb = new FacebookAuthenticationOptions
{
AuthenticationType = "Facebook",
SignInAsAuthenticationType = signInAsType,
AppId = ConfigurationManager.AppSettings["Facebook:AppId"],
AppSecret = ConfigurationManager.AppSettings["Facebook:AppSecret"]
};
fb.Scope.Add("email");
app.UseFacebookAuthentication(fb);
Then of course I've customized the AuthenticateLocalAsync method, but the claims I'm receiving only include name. No email claim.
Digging through the source code for identity server, I realized that there are some claims things happening to transform facebook claims, so I extended that class to debug into it and see if it was stripping out any claims, which it's not.
I also watched the http calls with fiddler, and I only see the following (apologies as code formatting doesn't work very good on urls. I tried to format the querystring params one their own lines but it didn't take)
(facebook.com)
/dialog/oauth
?response_type=code
&client_id=xxx
&redirect_uri=https%3A%2F%2Fidentity.[site].com%2Fid%2Fsignin-facebook
&scope=email
&state=xxx
(facebook.com)
/login.php
?skip_api_login=1
&api_key=xxx
&signed_next=1
&next=https%3A%2F%2Fwww.facebook.com%2Fv2.7%2Fdialog%2Foauth%3Fredirect_uri%3Dhttps%253A%252F%252Fidentity.[site].com%252Fid%252Fsignin-facebook%26state%3Dxxx%26scope%3Demail%26response_type%3Dcode%26client_id%3Dxxx%26ret%3Dlogin%26logger_id%3Dxxx&cancel_url=https%3A%2F%2Fidentity.[site].com%2Fid%2Fsignin-facebook%3Ferror%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied%26state%3Dxxx%23_%3D_
&display=page
&locale=en_US
&logger_id=xxx
(facebook.com)
POST /cookie/consent/?pv=1&dpr=1 HTTP/1.1
(facebook.com)
/login.php
?login_attempt=1
&next=https%3A%2F%2Fwww.facebook.com%2Fv2.7%2Fdialog%2Foauth%3Fredirect_uri%3Dhttps%253A%252F%252Fidentity.[site].com%252Fid%252Fsignin-facebook%26state%3Dxxx%26scope%3Demail%26response_type%3Dcode%26client_id%3Dxxx%26ret%3Dlogin%26logger_id%3Dxxx
&lwv=100
(facebook.com)
/v2.7/dialog/oauth
?redirect_uri=https%3A%2F%2Fidentity.[site].com%2Fid%2Fsignin-facebook
&state=xxx
&scope=email
&response_type=code
&client_id=xxx
&ret=login
&logger_id=xxx
&hash=xxx
(identity server)
/id/signin-facebook
?code=xxx
&state=xxx
I saw the code parameter on that last call and thought that maybe I could use the code there to get the access_token from the facebook API https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow
However when I tried that I get a message from the API telling me the code has already been used.
I also tried to change the UserInformationEndpoint to the FacebookAuthenticationOptions to force it to ask for the email by appending ?fields=email to the end of the default endpoint location, but that causes identity server to spit out the error "There was an error logging into the external provider. The error message is: access_denied".
I might be able to fix this all if I can change the middleware to send the request with response_type=id_token but I can't figure out how to do that or how to extract that access token when it gets returned in the first place to be able to use the Facebook C# sdk.
So I guess any help or direction at all would be awesome. I've spent countless hours researching and trying to solve the problem. All I need to do is get the email address of the logged-in user via IdentityServer3. Doesn't sound so hard and yet I'm stuck.
I finally figured this out. The answer has something to do with Mitra's comments although neither of those answers quite seemed to fit the bill, so I'm putting another one here. First, you need to request the access_token, not code (authorization code) from Facebook's Authentication endpoint. To do that, set it up like this
var fb = new FacebookAuthenticationOptions
{
AuthenticationType = "Facebook",
SignInAsAuthenticationType = signInAsType,
AppId = ConfigurationManager.AppSettings["Facebook:AppId"],
AppSecret = ConfigurationManager.AppSettings["Facebook:AppSecret"],
Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, ClaimValueTypes.String, "Facebook"));
return Task.FromResult(0);
}
}
};
fb.Scope.Add("email");
app.UseFacebookAuthentication(fb);
Then, you need to catch the response once it's logged in. I'm using the following file from the IdentityServer3 Samples Repository, which overrides (read, provides functionality) for the methods necessary to log a user in from external sites. From this response, I'm using the C# Facebook SDK with the newly returned access_token claim in the ExternalAuthenticationContext to request the fields I need and add them to the list of claims. Then I can use that information to create/log in the user.
public override async Task AuthenticateExternalAsync(ExternalAuthenticationContext ctx)
{
var externalUser = ctx.ExternalIdentity;
var claimsList = ctx.ExternalIdentity.Claims.ToList();
if (externalUser.Provider == "Facebook")
{
var extraClaims = GetAdditionalFacebookClaims(externalUser.Claims.First(claim => claim.Type == "urn:facebook:access_token"));
claimsList.Add(new Claim("email", extraClaims.First(k => k.Key == "email").Value.ToString()));
claimsList.Add(new Claim("given_name", extraClaims.First(k => k.Key == "first_name").Value.ToString()));
claimsList.Add(new Claim("family_name", extraClaims.First(k => k.Key == "last_name").Value.ToString()));
}
if (externalUser == null)
{
throw new ArgumentNullException("externalUser");
}
var user = await userManager.FindAsync(new Microsoft.AspNet.Identity.UserLoginInfo(externalUser.Provider, externalUser.ProviderId));
if (user == null)
{
ctx.AuthenticateResult = await ProcessNewExternalAccountAsync(externalUser.Provider, externalUser.ProviderId, claimsList);
}
else
{
ctx.AuthenticateResult = await ProcessExistingExternalAccountAsync(user.Id, externalUser.Provider, externalUser.ProviderId, claimsList);
}
}
And that's it! If you have any suggestions for simplifying this process, please let me know. I was going to modify this code to do perform the call to the API from FacebookAuthenticationOptions, but the Events property no longer exists apparently.
Edit: the GetAdditionalFacebookClaims method is simply a method that creates a new FacebookClient given the access token that was pulled out and queries the Facebook API for the other user claims you need. For example, my method looks like this:
protected static JsonObject GetAdditionalFacebookClaims(Claim accessToken)
{
var fb = new FacebookClient(accessToken.Value);
return fb.Get("me", new {fields = new[] {"email", "first_name", "last_name"}}) as JsonObject;
}
I created a web service and was able to send requests to it from a serverside Jaggery.js script with no problem. Then I created a WSDL Proxy Service inside WSO2 ESB and tested it using the "Try it!" feature.
After I redirected my serverside script from the original web service to its proxy inside ESB, I got the error in System Logs:
The endpoint reference (EPR) for the Operation not found is /services/BpmAdderProcessProxy.BpmAdderProcessProxyHttpSoap11Endpoint and the WSA Action = urn:anonOutInOpResponse. If this EPR was previously reachable, please contact the server administrator.
To see in detail what was happening I activated the "SOAP Message Tracer" of the ESB. Suddenly my serverside script could access the webservice via my ESB proxy. Then I deactivated the "SOAP Message Tracer" and the error message was back again. Is my serverside script correct? Or does the debugging tool modify behavior of debugged code?
I'm a JavaScript developer. Actually Jaggery and UES are targeted at people like me. I'm not supposed to look inside Java code, am I? Is there a forum where JavaScript developers discuss WSO2 UES and Jaggery?
My serverside code is as follows:
<%
var x = request.getParameter("x");
var y = request.getParameter("y");
//var sum = parseInt(x) + parseInt(y);
var sum = add(parseInt(x), parseInt(y));
response.content = {
success: true,
data: {
result: sum
}
};
function add(x, y) {
var ws = require('ws');
var stub = new ws.WSStub("http://02-128:8280/services/BpmAdderProcessProxy?wsdl");
var process = stub.services["BpmAdderProcessProxy"].operations["process"];
var payloadTemplate = process.payloadXML();
var payload = replaceQuestionMarks(payloadTemplate, arguments);
var resultXml = process.request(payload);
var resultValue = resultXml.children().text();
return parseInt(resultValue);
}
function replaceQuestionMarks(template, values) {
var i = 0;
return template.replace(
/\?/g,
function() {
return values[i++];
}
);
}
%>
In ESB v4.8.1, pass-through transport is enabled by default and it does not support SOAP body based dispatching (it does not build the message so it can't acces the body's first element to find the operation)
You can append the operation name to the endpoint url : http://host:8280/services/BpmAdderProcessProxy/OperationName
You can add this parameter in your proxy conf (BpmAdderProcessProxy) in WSO2 ESB : <parameter name="disableOperationValidation" locked="false">true</parameter>
You can edit wso2esb/repository/conf/axis2/axis2.xml and replace <handler class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher" name="SOAPMessageBodyBasedDispatcher"/>
with
<handler class="org.apache.synapse.core.axis2.SynapseSOAPMessageBodyBasedDispatcher" name="SOAPMessageBodyBasedDispatcher"/>