The server was not able to find the TwiML application associated with the App SID - node.js

Twilio voice call got error "The server was not able to find the TwiML application associated with the App SID"
followed step
1 - create the twiml app in console
2 - generated api keys
3 - integrated with code
var identity = req.body.identity;
const voiceGrant = new VoiceGrant({
outgoingApplicationSid: config.twilio.twiml_voice_sid,
pushCredentialSid: config.twilio.PUSH_SID
});
const token = new AccessToken(config.twilio.accountSid, config.twilio.API_KEY, config.twilio.API_KEY_SECRET);
token.addGrant(voiceGrant);
token.identity = identity;
res.send({
identity: identity,
token: token.toJwt()
});
Token generated successfully but when I try to use that token from ios side
I got following error in ios sdk
Error: Error Domain=com.twilio.voice.error Code=21218 "Application not found." UserInfo={NSLocalizedDescription=Application not found., NSLocalizedFailureReason=The server was not able to find the TwiML application associated with the App SID}
Thanks in advance

Got the solutions... I forgot to add callback URL in TwiML app
I just created a POST URL in my app and added that URL in TWIML App
Here is the code for POST URL
exports.makeCall = function (req, res) {
const VoiceResponse = require('twilio').twiml.VoiceResponse;
const response = new VoiceResponse();
const dial = response.dial();
dial.client(req.body.To);
res.send(response.toString());
};

Related

How to collect Instagram App Secret to generate a long-lived token?

On this page I generate the access token and with it I can publish the image on my Instagram:
For publish:
function InstagramPost() {
const access_token = 'GENERATE ACESS TOKEN';
const instagram_business_account = 'YYYYYYYYYYYYYYYYYY';
const image = 'https://upload.wikimedia.org/wikipedia/en/9/95/Test_image.jpg';
const text = 'Hello World';
var formData = {
'image_url': image,
'caption': text,
'access_token': access_token
};
var options = {
'method' : 'post',
'payload' : formData
};
const container = 'https://graph.facebook.com/v14.0/' + instagram_business_account + '/media';
const response = UrlFetchApp.fetch(container, options);
const creation = response.getContentText();
var data = JSON.parse(creation);
var creationId = data.id
var formDataPublish = {
'creation_id': creationId,
'access_token': access_token
};
var optionsPublish = {
'method' : 'post',
'payload' : formDataPublish
};
const sendinstagram = 'https://graph.facebook.com/v14.0/' + instagram_business_account + '/media_publish';
UrlFetchApp.fetch(sendinstagram, optionsPublish);
}
Now I want to take this access token and generate a long-lived one with it!
It asks for Instagram App Secret, but that path indicated (App Dashboard > Products > Instagram > Basic Display > Instagram App Secret) doesn't exist in App Dashboard!
I tried using the App secret as a parameter:
"https://graph.instagram.com/access_token
?grant_type=ig_exchange_token
&client_secret={App Secret Key}
&access_token={short-lived-access-token}"
But this error occurs:
Sorry, this content isn't available right now
The Facebook API is 100% accessible, so that's not the problem.
In Javascript/NodeJS I couldn't get it working at all (also on PostMan), I was using the request library.
Changed my code to:
const respLong = await axios.get(`https://graph.instagram.com/access_token?grant_type=ig_exchange_token&client_secret=${CLIENT_SECRET}&access_token=${accessTokenShort.toString()}`);
And magically this works. I can't tell you why what seems to be the exact same request in Postman and the request library doesn't work.
See pic of the url to get app secret (add your app ID) is: https://developers.facebook.com/apps/YOUR_APP_ID(number)/instagram-basic-display/basic-display/
There is a difference in approach between Basic Display and Instagram Graph API for Business Account.
So the way to convert a short-lived token to a long-lived token for Instagram Business Account is:
"https://graph.facebook.com/{graph-api-version}/oauth/access_token?
grant_type=fb_exchange_token&
client_id={app-id}&
client_secret={app-secret}&
fb_exchange_token={your-short-lived-access-token}"
Note that Instagram App Secret is not used, instead, use App Id and App Secret.

How to use googleapis google.auth.GoogleAuth() for google API service account in Twilio serverless function?

How to use googleapis google.auth.GoogleAuth() for google API service account in Twilio serverless function, since there is no FS path to provide as a keyFile value?
Based on the example here ( https://www.section.io/engineering-education/google-sheets-api-in-nodejs/ ) and here ( Google api node.js client documentation ) my code is based on the example here ( Receive an inbound SMS ) and looks like...
const {google} = require('googleapis')
const fs = require('fs')
exports.handler = async function(context, event, callback) {
const twiml = new Twilio.twiml.MessagingResponse()
// console.log(Runtime.getAssets()["/gservicecreds.private.json"].path)
console.log('Opening google API creds for examination...')
const creds = JSON.parse(
fs.readFileSync(Runtime.getAssets()["/gservicecreds.private.json"].path, "utf8")
)
console.log(creds)
// connect to google sheet
console.log("Getting googleapis connection...")
const auth = new google.auth.GoogleAuth({
keyFile: Runtime.getAssets()["/gservicecreds.private.json"].path,
scopes: "https://www.googleapis.com/auth/spreadsheets",
})
const authClientObj = await auth.getClient()
const sheets = google.sheets({version: 'v4', auth: authClientObj})
const spreadsheetId = "myspreadsheetID"
console.log("Processing message...")
if (String(event.Body).trim().toLowerCase() == 'KEYWORD') {
console.log('DO SOMETHING...')
try {
// see https://developers.google.com/sheets/api/guides/values#reading_a_single_range
let response = await sheets.spreadsheets.values.get({
spreadsheetId: spreadsheetId,
range: "'My Sheet'!B2B1000"
})
console.log("Got data...")
console.log(response)
console.log(response.result)
console.log(response.result.values)
} catch (error) {
console.log('An error occurred...')
console.log(error)
console.log(error.response)
console.log(error.errors)
}
}
// Return the TwiML as the second argument to `callback`
// This will render the response as XML in reply to the webhook request
return callback(null, twiml)
...where the Asset referenced in the code is for a JSON generated from creating a key pair for a Google APIs Service Account and manually copy/pasting the JSON data as an Asset in the serverless function editor web UI.
I see error messages like...
An error occurred...
{ response: '[Object]', config: '[Object]', code: 403, errors: '[Object]' }
{ config: '[Object]', data: '[Object]', headers: '[Object]', status: 403, statusText: 'Forbidden', request: '[Object]' }
[ { message: 'The caller does not have permission', domain: 'global', reason: 'forbidden' } ]
I am assuming that this is due to the keyFile not being read in right at the auth const declaration (IDK how to do it since all the example I see assume a local filepath as the value, but IDK how to do have the function access that file for a serverless function (my attempt in the code block is really just a shot in the dark)).
FYI, I can see that the service account has an Editor role in the google APIs console (though I notice the "Resources this service account can access" has the error
"Could not fund an ancestor of the selected project where you have access to view a policy report on at least one ancestor"
(I really have no idea what that means or implies at all, very new to this)). Eg...
Can anyone help with what could be going wrong here?
(BTW if there is something really dumb/obvious that I am missing (eg. a typo) just LMK in a comment so can delete this post (as it would then not serve any future value of others))
The caller does not have permission', domain: 'global', reason: 'forbidden
This actually means that the currently authenticated user (the service account) does ot have access to do what you are asking it to do.
You are trying to access a spread sheet.
Is this sheet on the service accounts google drive account? If not did you share the sheet with the service account?
The service account is just like any other user if it doesn't have access to something it cant access it. Go to the google drive web application and share the sheet with the service account like you would share it with any other user just use the service account email address i think its called client id its the one with an # in it.
delegate to user on your domain
If you set up delegation properly then you can have the service account act as a user on your domain that does have access to the file.
delegated_credentials = credentials.with_subject('userWithAccess#YourDomain.org')

Google Calendar API and Service Account permission error

I'm trying to integrate the Google Calendar API in my app.
So far i've managed to do this:
Created a new project on Cloud Platform
Enabled Calendar API
Added a new service account with role: Owner
Generated jwt.json
Granted domain-wide for that service account
Shared a calendar with that service account (modify rights)
Enabled in the GSuite the option for everyone out of the organisation to modify the events
Now, my code on node.js looks like this:
const { JWT } = require('google-auth-library');
const client = new JWT(
keys.client_email,
null,
keys.private_key,
['https://www.googleapis.com/auth/calendar']
);
const url = `https://dns.googleapis.com/dns/v1/projects/${keys.project_id}`;
const rest = await client.request({url});
console.log(rest);
The error I get is:
Sending 500 ("Server Error") response:
Error: Insufficient Permission
Anyone has any ideea? This gets frustrating.
How about this modification?
I think that in your script, the endpoint and/or scope might be not correct.
Pattern 1:
In this pattern, your endpoint of https://dns.googleapis.com/dns/v1/projects/${keys.project_id} is used.
Modified script:
const { JWT } = require("google-auth-library");
const keys = require("###"); // Please set the filename of credential file of the service account.
async function main() {
const calendarId = "ip15lduoirvpitbgc4ppm777ag#group.calendar.google.com";
const client = new JWT(keys.client_email, null, keys.private_key, [
'https://www.googleapis.com/auth/cloud-platform' // <--- Modified
]);
const url = `https://dns.googleapis.com/dns/v1/projects/${keys.project_id}`;
const res = await client.request({ url });
console.log(res.data);
}
main().catch(console.error);
In this case, it is required to enable Cloud DNS API at API console. And it is required to pay. Please be careful with this.
I thought that the reason of your error message of Insufficient Permission might be this.
Pattern 2:
In this pattern, as a sample situation, the event list is retrieved from the calendar shared with the service account. If the calendar can be used with the service account, the event list is returned. By this, I think that you can confirm whether the script works.
Modified script:
const { JWT } = require("google-auth-library");
const keys = require("###"); // Please set the filename of credential file of the service account.
async function main() {
const calendarId = "###"; // Please set the calendar ID.
const client = new JWT(keys.client_email, null, keys.private_key, [
"https://www.googleapis.com/auth/calendar"
]);
const url = `https://www.googleapis.com/calendar/v3/calendars/${calendarId}/events`; // <--- Modified
const res = await client.request({ url });
console.log(res.data);
}
main().catch(console.error);
Note:
This modified script supposes that you are using google-auth-library-nodejs of the latest version.
Reference:
JSON Web Tokens in google-auth-library-nodejs

How to do Sign-In with Keycloak with Actions on Google?

I'm trying to do a sign-in through an action on Google with Google Assistant (on Dialogflow), but it fails for some reason. The sign-in works (it asks for my username and password), then the browser disappears and Google Assistant says the authentication has failed.
created a new client in Keycloak and added redirect URL and allowed web origins
Added Account Linking on Actions Console with authorization URL, Client ID, Secret, Token URL (this one might be a wrong url, I tried one but I couldn't find the correct URL)
Created intents deployed on Firebase with Dialogflow that asks for authentication
'use strict';
// Import the Dialogflow module from the Actions on Google client library.
const {dialogflow, SignIn} = require('actions-on-google');
// Import the firebase-functions package for deployment.
const functions = require('firebase-functions');
// Instantiate the Dialogflow client.
const app = dialogflow({clientId: 'my-client-id', debug: true});
app.intent('sign in', (conv, {person}) => {
conv.ask(new SignIn(""));
const name = person.name;
conv.close('Hello ' + name);
});
// Create a Dialogflow intent with the `actions_intent_SIGN_IN` event.
app.intent('Get Signin', (conv, params, signin) => {
if (signin.status === 'OK') {
const payload = conv.user.profile.payload;
conv.ask(`I got your account details, ${payload.name}. What do you want to do next?`);
} else {
conv.ask(`I won't be able to save your data, but what do you want to do next?`);
}
});
// Set the DialogflowApp object to handle the HTTPS POST request.
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
Basically when the person triggers the sign in function, it asks for a name. Then it should login, and print the given name again and close the conversation.
The sign in popup just disappears after logging in, and the Google Assistant tells me that something went wrong and that I should try later. No errors were found or atleast, I couldn't find them. I suspect my Account Linking settings are wrong though, but I'm not sure.
It works now by using the correct Token URL found in https://keycloakurl:keycloakport/auth/realms/realm-name/.well-known/openid-configuration.

Getting Error: unauthorized_client when trying to authorize script

I've created service account with domain wide delegation and its scopes (in Admin console and Developer console) as described in documentation. I've been trying this for a week now and I am stuck. This is my code:
const google = require('googleapis');
const gmail = google.gmail('v1');
const directory = google.admin('directory_v1');
const scopes = [
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/admin.directory.user.readonly'
];
const key = require('./service_key.json');
var authClient = new google.auth.JWT(
key.client_email,
key,
key.private_key,
scopes,
"kruno#example.com"
);
authClient.authorize(function(err, tokens){
if (err) {
console.log(err);
return;
}
// API call methods here...
});
I get this error:
Error: unauthorized_client
I am unable to understand:
Is this proper technique for calling Google API methods from server-side scripts without any user interaction? (under domain only)
How do service account and actual user account communicate this way?
I heard about callback URI, am I missing it?
I think you are missing the final step which is giving access to your application in the control panel of your domain.
You can follow doc properly to activate it with your application
https://developers.google.com/+/domains/authentication/delegation
Also you can start with your first call step here
https://developers.google.com/adwords/api/docs/guides/first-api-call

Resources