Currently I can send a message with Google Business Messages API from an agent to a user from NodeJS code.
const bmApi = new businessmessages.businessmessages_v1.Businessmessages({});
This requires an auth client for a given service account key/secret.
const auth = new GoogleAuth({
keyFilename: '/home/my-keyfile.json',
scopes: 'https://www.googleapis.com/auth/businessmessages',
});
const authClient = await auth.getClient();
// and logic to send message
However the key/secret is hard-coded at the moment.
But at this point in the flow I have the access token.
And want to use that instead of the .json file.
But it will not accept the access token.
Another approach is to directly call the REST interface.
https://developers.google.com/business-communications/business-messages/guides/how-to/message/send
curl -X POST https://businessmessages.googleapis.com/v1/conversations/__CONVERSATION_ID__/messages \
-H "Content-Type: application/json" \
-H "User-Agent: curl/business-messages" \
-H "$(oauth2l header --json ./service_account_key.json businessmessages)" \
-d "{
'messageId': '$(uuidgen)',
'text': 'Hello world!',
'representative': {
'avatarImage': 'https://developers.google.com/identity/images/g-logo.png',
'displayName': 'Chatbot',
'representativeType': 'BOT'
}
}"
Added a header with token.
access_token: <access-token>
But again no joy.
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
I know this should work as we do it for calls to Google Play Store:
try {
let response = await this.httpClient.post({
url: `${process.env.PLAYSTORE_URL}/${packageName}/reviews/${reviewId}:reply`,
body : {
"replyText" : replyText
},
query: {
access_token: access_token <----
}
});
Any help would be much appreciated.
i think you need to use the variable that match the current CONVERSATION_ID in the url path, with the currently one of each agent message received.
Example:
curl -X POST https://businessmessages.googleapis.com/v1/conversations/$(uuidgen)/messages \
-H "Content-Type: application/json" \
-H "User-Agent: curl/business-messages" \
-H "$(oauth2l header --json ./service_account_key.json businessmessages)" \
-d "{
'messageId': '$(uuidgen)',
'text': 'Hello world!',
'representative': {
'avatarImage': 'https://developers.google.com/identity/images/g-logo.png',
'displayName': 'Chatbot',
'representativeType': 'BOT'
}
}"
Related
I am trying to integrating Paypal on my application. This application will allow people to sell tokens between them. So the money doesn't go to me, but to the payees.
I first used smart button directly on my front-end (https://developer.paypal.com/docs/checkout/integrate/). I just had to add a payee field and it worked perfectly fine. 0 bug, no issue.
But now, I want to make the integration on a NodejS back-end, because it is more secure for me.
And despite I am doing exactly the same thing (create order -> capturing order), I am getting this error :
{"name":"NOT_AUTHORIZED","details": [{
"issue":"PAYEE_NOT_CONSENTED","description":"Payee does not have appropriate consent to
allow the API caller to process this type of transaction on their behalf. Your current
setup requires the 'payee' to provide a consent before this transaction can be
processed successfully."
}],
"message": "Authorization failed due to insufficient permissions.",
"debug_id":"300756d694c77","links": [{
"href":"https://developer.paypal.com/docs/api/orders/v2/#error-PAYEE_NOT_CONSENTED",
"rel":"information_link","method":"GET"
}]
}
Why ? Why it has no issue doing this kind of operation with smart button, but I can't do it with the checkout sdk on nodejs and need the payee consent ?
Whats the difference ?
Needing the consent or any kind of operation from the payee is really annoying for me, because I need payee to sell their tokens with the minimal of action to do it.
And I don't see whats the difference between doing it with smart button, or with a back-end.
Btw here is my code :
const paypal = require('#paypal/checkout-server-sdk')
if (process.env.NODE_ENV === "prod") {
APP_SETTINGS = require('src/PRIVATE_APP_SETTINGS.json').prod;
var environment = new paypal.core.LiveEnvironment(APP_SETTINGS.paypal.paypal_client_id, APP_SETTINGS.paypal.paypal_secret);
} else {
APP_SETTINGS = require('src/PRIVATE_APP_SETTINGS.json').dev;
var environment = new paypal.core.SandboxEnvironment(APP_SETTINGS.paypal.paypal_client_id, APP_SETTINGS.paypal.paypal_secret);
}
var client = new paypal.core.PayPalHttpClient(environment);
function createPayment(info)
{
const body = {
intent: 'CAPTURE',
purchase_units: [{
amount:
{
value: info.usdAmount,
currency_code: 'USD'
},
payee: {
email_address: info.paypalEmail
}
}],
}
let request = new paypal.orders.OrdersCreateRequest()
request.requestBody(body)
return client.execute(request).then(res => {
return {res: true, id: res.result.id}
}).catch(err => {
if (err) {
console.error(err.message);
}
return {res: false}
})
}
function executePayment(info)
{
console.log(info)
const request = new paypal.orders.OrdersCaptureRequest(info.orderId)
request.requestBody({})
return client.execute(request).then((result) => {
console.log("Payment suceed")
return {res: true}
}).catch(err => {
if (err) {
console.error(err);
}
return {res: false}
})
}
Oddly, you don't seem to be doing anything wrong. I tested the exact same thing except using a simple curl integration rather than node, and everything worked as expected. Here are the curl requests so you can validate whether it works for you...
#!/bin/bash
access_token=$(curl -s https://api-m.sandbox.paypal.com/v1/oauth2/token \
-H "Accept: application/json" \
-H "Accept-Language: en_US" \
-u "clientid:secret" \
-d "grant_type=client_credentials" | python3 -c "import sys, json; print(json.load(sys.stdin)['access_token'])")
echo $access_token
#!/bin/bash
access_token=$(./curl_rest_gettoken);
curl $# -s https://api-m.sandbox.paypal.com/v2/checkout/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $access_token" \
-d '{
"intent": "CAPTURE",
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "5"
},
"payee": {
"email_address": "......#business.example.com"
}
}
],
"application_context": {
"return_url": "https://www.yahoo.com"
}
}' | python -mjson.tool
#!/bin/bash
access_token=$(./curl_rest_gettoken);
curl -v -s -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders/$1/capture \
-H "Content-Type:application/json" \
-H "Authorization: Bearer $access_token" | python -mjson.tool
I am new to basic authentication and tokens.
I have been playing around with postman in order to get a token using basic authentication and then passing the token as a bearer token to access another endpoint. I wanted to know how I would code this into api calls using node and express.
I know that for Basic auth I need to encode the client id and secret into base64
curl --request POST \
--url http://localhost:8080/token/ \
--header 'authorization: Basic ***' \
--header 'content-type: application/x-www-form-urlencoded' \
--data
grant_type=credentials
The token I get from the above call I want to pass onto the below call
curl --request POST \
--url http://localhost:8080/login \
--header 'authorization: Bearer ***' \
--header 'content-type: application/x-www-form-urlencoded' \
--data
user=1
How would this like as code in a node application
I would recommend json web token aka jwt for this purpose.
Right now I code REST API in express, mongodb, and I am using jwt for auth.
Since I dont use any frontend framework or lib, I use cookie for jwt token storage.
const jwt = require('jsonwebtoken');
const generateToken = (res, id, auth_level) => {
const token = jwt.sign({id,
auth_level
}, process.env.JWT_KEY, {
expiresIn: '7d'
});
return res.cookie('token', token, {
expires: new Date(Date.now() + 1000 * 60 * 15),
secure: false,
httpOnly: true,
});
};
module.exports = generateToken
In this example I call this function on sucessful login try. And after that on every route access, using middleware I try to resolve if user have this token and try to resolve token.
const jwt = require('jsonwebtoken');
// Verify user token from cookie
const verifyToken = async (req, res, next) => {
// Get token from cookie named token
const token = req.cookies.token || '';
try {
// Check if cookie exists, maybe expired maybe user didnt have one - no login
if (!token) {
return next();
}
// Decrypt users jwt token and get information
const decrypt = await jwt.verify(token, process.env.JWT_KEY);
// Pass that infomation to request user object
req.user = {
id: decrypt.id,
auth_level: decrypt.auth_level,
test: 'test'
};
// Continue with exectution of app
return next();
} catch (err) {
return res.status(500).json(err.toString());
}
};
module.exports = verifyToken;
If this token is valid, I pass custom user object to req object.
After this I protect routes with custom middlewares. Code is inspired by this tutorial, would recommend it.
The API says that status code 400 is probably syntax error, but I wasn't able to find it. I already have the authentication code and app credentials, and the url is registered.
I've tried with and without qs.
exports.getAccessToken = (req, res, next) => {
let payload = req.body;
let request_body = qs.stringify({
"grant_type": "authorization_code",
"code": payload.code,
"redirect_uri": linkedin.redirect_uri,
"client_id": linkedin.clientId,
"client_secret": linkedin.clientSecret
});
let config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
axios.post("https://www.linkedin.com/oauth/v2/accessToken", request_body, config).then(
response => {
res.json(response.data);
},
error => {
res.json(error);
}
);
}
You are using the wrong url to get accesstoken.
Step 1
First you need to use following url
curl -X GET \
'https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=[CLIENT_ID]&redirect_uri=https://getpostman.com/oauth2/callback&state=CA&scope=r_emailaddress%20r_ads%20w_organization_social%20rw_ads%20r_basicprofile%20r_liteprofile%20r_ads_reporting%20r_organization_social%20rw_organization_admin%20w_member_social' \
-H 'Postman-Token: 1c7d6199-f41b-43bc-b9ae-10d4bde0d968' \
-H 'cache-control: no-cache'
Response contains code which needs to used in Step 2
Step 2
use CODE received from Step 1
curl -X GET \
'https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code=[CODE]&redirect_uri=https://getpostman.com/oauth2/callback&client_id=[CLIENT_ID]&client_secret=[CLIENT_SECRET]' \
-H 'Postman-Token: f9542a93-fc9d-4cc4-aa38-a10f6cf2eb6f' \
-H 'cache-control: no-cache'
This will give you the access Token
I have following CURL command
curl -u YOUR_API_KEY:x \
-H 'Content-Type: application/json' \
-X POST \
-d '{"first_name":"Tony", "kind":"person", "contact_name":"Stark"}' \
'https://ACCOUNT_NAME.quadernoapp.com/api/contacts.json'
I want do this request in NodeJS using the request module.
This is the code I have written.
var options = {
uri: 'https://ACCOUNT_NAME.quadernoapp.com/api/contacts.json',
json: true,
auth: {
user: 'YOUR_API_KEY'
}
data: {"first_name" : "Tonsdfasdy", "kind":"peasdfarson", "contact_name":"Staasdfadfadrk"}
}
request.post(options, function cb(){})
But it is not authenticatd properly. What is the error here?
You're authenticating using HTTP Basic authentication in your cURL command, where username and password are provided with the -u option and separated by :, so you need to provide your code with the password, like so :
var options = {
uri: 'https://ACCOUNT_NAME.quadernoapp.com/api/contacts.json',
json: true,
auth: {
user: 'YOUR_API_KEY',
password: 'x'
},
body: {
first_name : "Tonsdfasdy", kind:"peasdfarson", contact_name:"Staasdfadfadrk"
}
}
request.post(options, function cb(){})
And please try to pass your JSON object in an attribute named body rather than data (it will be transformed to a JSON string thanks to the json: trueoption).
You may also want to check this one : how to do Auth in node.js client
Hope this helps!
Update
Using emulator as channelId and updating to SDK 3.13.1 works for me.
App ID: 8c082f92-fb38-4841-a29f-339eb315f7aa
SDK Platform: Node.js
SDK Version: 3.13.1
Active Channels: Facebook
Deployment Environment: ngrok
Issue Description
I tried following steps mentioned in this link. This has two parts. One is creating the token and second is to send a message to the bot. POSTMAN request leads to 500 Internal Server Error and ERROR: ChatConnector: receive - invalid signing key or OpenId metadata document in the code.
Code Example
Create token
curl -X POST https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token -H 'cache-control: no-cache' -H 'content-type: application/x-www-form-urlencoded' -H 'postman-token: 792660ab-b1aa-0cbd-edab-9b3847c170d5' -d 'grant_type=client_credentials&client_id=8c082f92-fb38-4841-a29f-339eb315f7aa&client_secret=vxcihBT2679%7C(%23puEXBPT1!&scope=8c082f92-fb38-4841-a29f-339eb315f7aa%2F.default'
Send a message
curl -X POST https://1c36f336.ngrok.io/api/messages -H 'authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ing0Nzh4eU9wbHNNMUg3TlhrN1N4MTd4MXVwYyJ9.eyJhdWQiOiI4YzA4MmY5Mi1mYjM4LTQ4NDEtYTI5Zi0zMzllYjMxNWY3YWEiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vZDZkNDk0MjAtZjM5Yi00ZGY3LWExZGMtZDU5YTkzNTg3MWRiL3YyLjAiLCJpYXQiOjE1MTIxOTg2NjcsIm5iZiI6MTUxMjE5ODY2NywiZXhwIjoxNTEyMjAyNTY3LCJhaW8iOiJZMk5nWUZEOHRySzgvZk9XcDF2L1BMM2JibnRGRGdBPSIsImF6cCI6IjhjMDgyZjkyLWZiMzgtNDg0MS1hMjlmLTMzOWViMzE1ZjdhYSIsImF6cGFjciI6IjEiLCJ0aWQiOiJkNmQ0OTQyMC1mMzliLTRkZjctYTFkYy1kNTlhOTM1ODcxZGIiLCJ1dGkiOiJXLWdYbXpPSkxVYWdzSFZUbXBJd0FBIiwidmVyIjoiMi4wIn0.G705tzQIU5Mh6IROtXkIwm8Q9AKZ_q-VOtJuuozqP-ekhWoKc0HpcdhgBlnaMsMHKoM0RWhUlXn27xCfK46vEE9IZlkjcAh7huhvlWNtW8IP5w7QiL3JCSTYKCtBXZq-VKeWctNWR9M8Y9Ry4dyiEdcDMcHWrbOUqR6nXvlwG76GSR4YilqzMWdSW6t5Pep3hwOw07DSargYP0pDAnWAg3pWHnhcJ185533I1VVXEIuc_CK-RmP9qhUAScEbYkpp_7l75bVWzxKv-3E1UOG4SBj6UzfH47X5kwz_njn1kAJcrqBvP3s_CjS4qUdDSNARtxGZ3UQWj49UBKosqar0dg' -H 'cache-control: no-cache' -H 'content-type: application/json' -H 'postman-token: 3a74ce00-2da7-d674-5e4c-083f54ed30ff' -d '{ "type": "message", "id": "mid.$cAAGEkG8MNm1mOEBe-lgBvsWZbQUc", "channelId" : "test", "conversation": { "id": "100023023852067-526013297749070"}, "from": { "id": "100023023852067" }, "recipient": { "id": "526013297749070" }, "serviceUrl": "https://1c36f336.ngrok.io", "text": "Hi message from postman !!!" }'
Reproduction Steps
Using the two curl requests mentioned above. This should lead to the mentioned issue.
Expected Behavior
POSTMAN request should lead to 202 Accepted and bot receiving the message. Am I missing something or is there something wrong in the process? I saw some issues on load testing but none of them helped.
Actual Results
POSTMAN request leads to 500 Internal Server Error and ERROR: ChatConnector: receive - invalid signing key or OpenId metadata document in the code.
I was able to get this to work after:
1) adding a custom state client to the bot using botbuilder-azure
2) exposing the message sink, for receiving the bot response
3) changing the channelId to "emulator" (apparently node sdk doesn't handle 'test' channel)
curl -X POST https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token -H "content-type: application/x-www-form-urlencoded" -d "grant_type=client_credentials&client_id=MyMicrosoftAppId&client_secret=MyMicrosoftAppPassword&scope=MyMicrosoftAppId%2F.default"
curl -X POST https://e84a2f49.ngrok.io/api/messages -H "authorization: Bearer TokenFromPreviousCall" -d "{ \"type\": \"message\", \"id\": \"mid.$cAAGEkG8MNm1mOEBe-lgBvsWZbQUc\", \"channelId\" : \"emulator\", \"conversation\": { \"id\": \"100023023852067-526013297749070\"}, \"from\": { \"id\": \"100023023852067\" }, \"recipient\": { \"id\": \"526013297749070\" }, \"serviceUrl\": \"https://e84a2f49.ngrok.io\", \"text\": \"Hi message from postman !!!\" }"
Here is the app, for reference:
var restify = require('restify');
var builder = require('botbuilder');
var azure = require('botbuilder-azure');
var sqlConfig = {
userName: 'SqlServerUserId',
password: 'SqlServerPassword',
server: 'mySqlServer.net',
enforceTable: true,
options: {
database: 'BotDatabaseName',
table: 'BotDataTableName',
encrypt: true,
rowCollectionOnRequestCompletion: true
}
}
var sqlClient = new azure.AzureSqlClient(sqlConfig);
var sqlStorage = new azure.AzureBotStorage({ gzipData: false }, sqlClient);
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3980, function () {
console.log('%s listening to %s', server.name, server.url);
});
var connector = new builder.ChatConnector({
appId: "MyAppId",
appPassword: "MyAppPassword"
});
server.post('/api/messages', connector.listen());
var bot = new builder.UniversalBot(connector, function (session) {
session.send("You said: %s", session.message.text);
}).set('storage', sqlStorage);;
//message sink
server.post("/v3/conversations/:conversationId/activities/:activityId", function(Request, Response, next) {
next();
});
Not sure if this will help, but if you're using the same "fromId" in every request for your load test, it might be relevant.
Previously I've had a similar issue that required me to use a unique "fromId" in each request, else a series of requests in rapid succession start to fail.
I raised this issue on github, and although it says "followed up offline" and "problem was with the bot code", it was actually due to using the same "fromId" in every request.
The issue is here: https://github.com/Microsoft/BotBuilder/issues/1176
After I added extended field I got same error.
Not sure how that is related but replace
server.use(bodyParser.urlencoded({ extended: true }));
to:
server.use(bodyParser.urlencoded());
fixed the issue.