I cannot count how many times I sweared on CORS.
Right now we are trying to access the outlook API to send emails and stuff. We follow the tutorial, do everything on Postman and that works. Now we want to implement it in our Angular 2 application with the following code:
requestAccessToken(code: string)
{
if (code) {
var headers = new Headers();
headers.append("Content-Type", 'application/x-www-form-urlencoded');
var requestoptions = new RequestOptions({
headers: headers,
withCredentials: false // tried true too
})
let body = `grant_type=authorization_code&
redirect_uri=http://localhost:4200&
code=`+ code + `&
client_id=4e...ab&
client_secret=CE.....BC`
this.http.post("https://login.microsoftonline.com/common/oauth2/v2.0/token", body, requestoptions).subscribe((data) =>
{
console.log("data: " + data);
},
error =>
{
console.log("error: " + error);
});
}
}
Our response looks like this:
{
"token_type":"Bearer",
"scope":"calendars.read calendars.read.shared calendars.readwrite calendars.readwrite.shared contacts.read
contacts.read.shared mail.read
user.read",
"expires_in":3599,"ext_expires_in":0,
"access_token":"ey...NjQ",
"refresh_token":"OAQABAAA...Fd8JA"
}
Which is exactly but I want, but however I cannot extract the token out of it and my browser logs the following:
As you can see, the error is logged and not the data and Chrome complains about CORS. I'm really stuck and the only thing the internet says is to change server settings, which is of course not possible with the URL login.microsoftonline.com
Related
I am using the payment_intent API to generate payment intent for payment sheet initialization.
As per the document, payment_intent is the POST method. Showing different errors in android and iOS.
https://stripe.com/docs/api/payment_intents/create
Note:- It's working in postman not working on mobile.
Case 1 Android
It is not working with the POST method. It worked with the GET method this is weird.
Case 2 iOS
It is not working with the GET and POST methods both.
With POST received the following error
_response": "{
\"error\": {
\"code\": \"parameter_missing\",
\"doc_url\": \"https://stripe.com/docs/error-codes/parameter-missing\",
\"message\": \"Missing required param: amount.\",
\"param\": \"amount\",
\"type\": \"invalid_request_error\"
}
}
With GET method received the following error
"_response":"resource exceeds maximum size"
End Point URL:-
let data = JSON.stringify({
customer: customerId,
currency: 'inr',
amount: 1000,
'automatic_payment_methods[enabled]': 'true',
});
let config = {
method: 'GET',
url: 'https://api.stripe.com/v1/payment_intents',
headers: {
Authorization:
'Bearer sk_test_DmXI7Jw1PnJAWYps3iCpvKkttIGX00pPfGLTjj',
'Content-Type': 'application/x-www-form-urlencoded',
},
data: data,
};
axios(config)
.then(function (response) {
console.info(JSON.stringify(response));
})
.catch(function (error) {
console.error('-----', error.response);
});
Following this document
https://stripe.com/docs/payments/accept-a-payment?platform=react-native&ui=payment-sheet#react-native-flowcontroller
https://stripe.com/docs/api/payment_intents/create
Added snack URL to reproduce the issue.
https://snack.expo.dev/#vishaldhanotiya/stripe-payment-intent
Error Log
To clarify a few things:
1/ You shared your (test mode) secret key in your code snippet, please delete that and roll your API keys (https://stripe.com/docs/keys#keeping-your-keys-safe).
2/ Your iOS/Android apps should not be making requests to Stripe's APIs directly with your secret API key, as that means you are bundling your secret key with your apps which means anyone running your app has access to your secret key.
Instead, you need to make requests from your iOS app to your server and your server should use Stripe's server-side libraries to make requests to Stripe's APIs. Your iOS/Android apps can only make requests with your publishable key.
3/ The PaymentIntent endpoint supports both POST and GET. You can create a PaymentIntent by POSTing to the /v1/payment_intents endpoint, you retrieve a single PaymentIntent with a GET to the /v1/payment_intents/:id endpoint and you list PaymentIntents with a GET to the /v1/payment_intents endpoint.
4/ The error in your POST request shows "Missing required param: amount." so you need to debug your code to make sure the amount parameter is getting through. You can use Stripe's Dashboard Logs page https://dashboard.stripe.com/test/logs to debug what parameters your code is sending to Stripe's API.
Finally, I found a solution. The issue occurred because I am send parameters without encoding.
I found a solution from this link
https://stackoverflow.com/a/58254052/9158543.
let config = {
method: 'post',
url: 'https://api.stripe.com/v1/payment_intents',
headers: {
Authorization:
'Bearer sk_test_51J3PfGLTjj',
'Content-Type': 'application/x-www-form-urlencoded'
}
};
let paymentDetail = {
customer: 'cus_MSiYLjtdaJPiCW',
currency: 'USD',
amount: 100,
'automatic_payment_methods[enabled]': true
};
let formBody: any = [];
for (let property in paymentDetail) {
let encodedKey = encodeURIComponent(property);
let encodedValue = encodeURIComponent(paymentDetail[property]);
formBody.push(encodedKey + '=' + encodedValue);
}
formBody = formBody.join('&');
const result = await axios
.post('https://api.stripe.com/v1/payment_intents', formBody, {
headers: config.headers
})
.then(function (response) {
console.info(JSON.stringify(response));
})
.catch(function (error) {
console.error('-----', error.response);
});
So I'm trying to make a google action using Dialogflow that requires an external API. I've always used jQuery .getJSON() to make API calls, so I had no idea how to do this. After searching this up online, I found a way to do this using vanilla javascript (I also tested the way on my website and it worked fine). The code for that is below:
function loadXMLDoc() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
console.log(xmlhttp.responseText);
}
};
xmlhttp.open("GET", "https://translate.yandex.net/api/v1.5/tr.json/translate?lang=en-es&key=trnsl.1.1.20190105T052356Z.7f8f950adbfaa46e.9bb53211cb35a84da9ce6ef4b30649c6119514a4&text=eat", true);
xmlhttp.send();
}
The code worked fine on my website, but as soon as I added it to the Dialogflow, it would give me the error
XMLHttpRequest is not defined
Obviously that happened because I never defined it (using var), except it worked without me doing anything. So then, I tried adding this line
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
to the code, and it stopped giving me the error (because I defined XMLHttpRequest). But then, my code wouldn't work.
TL;DR: How can I make an external API call using Dialogflow fulfillment?
You can use https. But make sure that you upgrade to Blaze Pay(or any other plans) to make external API calls, else you will receive an error such as
Error:
Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions.
Code to make external api call,
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
"use strict";
const functions = require("firebase-functions");
const { WebhookClient } = require("dialogflow-fulfillment");
const { Card, Suggestion } = require("dialogflow-fulfillment");
const https = require("https");
process.env.DEBUG = "dialogflow:debug"; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(
(request, response) => {
const agent = new WebhookClient({ request, response });
console.log(
"Dialogflow Request headers: " + JSON.stringify(request.headers)
);
console.log("Dialogflow Request body: " + JSON.stringify(request.body));
function getWeather() {
return weatherAPI()
.then(chat => {
agent.add(chat);
})
.catch(() => {
agent.add(`I'm sorry.`);
});
}
function weatherAPI() {
const url =
"https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22";
return new Promise((resolve, reject) => {
https.get(url, function(resp) {
var json = "";
resp.on("data", function(chunk) {
console.log("received JSON response: " + chunk);
json += chunk;
});
resp.on("end", function() {
let jsonData = JSON.parse(json);
let chat = "The weather is " + jsonData.weather[0].description;
resolve(chat);
});
});
});
}
function welcome(agent) {
agent.add(`Welcome to my agent!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
let intentMap = new Map();
intentMap.set("Default Welcome Intent", welcome);
intentMap.set("Default Fallback Intent", fallback);
intentMap.set("Weather Intent", getWeather);
agent.handleRequest(intentMap);
}
);
This article is a diamond! It really helped to clarify what's going on and what's required in Dialogflow fullfilments.
A small suggestion is to gracefully catch the error in the connection to the webservice:
function weatherAPI() {
const url = "https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22";
return new Promise((resolve, reject) => {
https.get(url, function(resp) {
var json = "";
resp.on("data", function(chunk) {
console.log("received JSON response: " + chunk);
json += chunk;
});
resp.on("end", function() {
let jsonData = JSON.parse(json);
let chat = "The weather is " + jsonData.weather[0].description;
resolve(chat);
});
}).on("error", (err) => {
reject("Error: " + err.message);
});
});
}
I am trying to link to the account :
Here is my google cloud function
var AuthHandler = function() {
this.googleSignIn = googleSignIn;
this.googleSignInCallback = googleSignInCallback;
}
function googleSignIn(req, res, next) {
passport = req._passport.instance;
passport.authenticate('google',{scope: 'https://www.googleapis.com/auth/userinfo.email',
state:"google",response_type:"token"},
function(err, user, info) {
console.log(user);
})(req,res,next);
};
function googleSignInCallback(req, res, next) {
passport = req._passport.instance;
passport.authenticate('google',function(err, user, info) {
if(err) {
return next(err);
}
if(!user) {
return res.redirect('http://localhost:8000');
}
console.log(user._json.token);
// /res.redirect('/');
res.redirect('https://oauth-redirect.googleusercontent.com/r/xxxxxx#access_token=' + user._json.token + '&token_type=bearer&state=google')
})(req,res,next);
};
module.exports = AuthHandler;
In google Action Console :
I have created the implicit flow and gave my authorisation url as follows:
https://[region]-[projectid].cloudfunctions.net/[functionname]/auth/google
Error :
this is the browser Url
https://assistant.google.com/services/auth/handoffs/auth/complete?state=xxxx&code=xxxxxx
on which the following error is displayed
The parameter "state" must be set in the query string.
Update 1
Before starting this implementation , i have followed this Solution to create the Authentication.
Problems in this Approach :
1.As stated in the Documentation it is not redirecting to google.com and i'm unable to access the token using the APIAI SDK in javascript. but still i can see the Access token in emulator . for better understanding adding images
Here is my simulator O/P
{
"response": {
"debug": {
"agentToAssistantDebug": {
"assistantToAgentDebug": {
"assistantToAgentJson": "{"accessToken\":\"xxxxxx\""
}
},
"errors": []
}
Update 2 :
So i have started creating with implicit flow and here is my complete repo
After battling with it i have achieved it , as there is no proper articles about creation of own Oauth Server that implements the Google Action , this might helpful for future users.
Authorization Endpoint
app.get('/authorise', function(req, res) {
req.headers.Authorization = 'Bearer xxxxxxxxxxx';
// with your own mechanism after successful
//login you need to create a access token for the generation of
//authorization code and append it to this header;
var request = new Request(req);
var response = new Response(res);
oauth.authorize(request, response).then(function(success) {
// https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID?
//code=AUTHORIZATION_CODE&state=STATE_STRING
var toredirect = success.redirectUri +"?code="+success.code
+"&state="+request.query.state ;
return res.redirect(toredirect);
}).catch(function(err){
res.status(err.code || 500).json(err)
}) });
Token Endpoint :
app.all('/oauth/token', function(req,res,next){
var request = new Request(req);
var response = new Response(res);
oauth
.token(request,response)
.then(function(token) {
// Todo: remove unnecessary values in response
return res.json(token)
}).catch(function(err){
return res.status(500).json(err)
})
});
After creation of this endpoints publish to the Google Cloud functions . I have used MYSQL as the DB using SEQUELIZE and Oauth-Server , if anyone need those models , will share it through repo .
With this you can able to link account using your own Server which implements
Auth tokens and Access Tokens
I think the problem is that the URL on this line isn't sending the parameters as query parameters, they're sending them as part of the anchor:
res.redirect('https://oauth-redirect.googleusercontent.com/r/xxxxxx#access_token=' + user._json.token + '&token_type=bearer&state=google')
You should replace the # with a ?, as illustrated here:
res.redirect('https://oauth-redirect.googleusercontent.com/r/xxxxxx?access_token=' + user._json.token + '&token_type=bearer&state=google')
I could use some guidens, sending an object from my angular 2 application to the Web API.
I know how to GET objects from the Web Api, to my angular 2 application, but can't seem to figure out how the post method works or even if I should use the http.post methodd.
My angular 2 application has the following method:
sendUpdatdReservation(updatedReservation: Reservation) {
var result;
var objectToSend = JSON.stringify(updatedReservation);
this.http.post('http://localhost:52262/api/postbookings', objectToSend)
.map((res: Response) => res.json()).subscribe(res => result = res);
console.log(result);
}
The "updatedReservation" is an object, which I convert to JSON.
The Web api can be reached by the following address:
httl://localhost:52262/api/postbookings
Web Api controller:
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class PostBookingsController : ApiController
{
[AcceptVerbs()]
public bool ConfirmBooking(Booking booking)
{
return true;
}
}
What I'm trying to do is to send the object, update my database based on the changes values that the object has. Then send back true or false if this is a confirmation or not so I can redirect to confirmation page.
Do any know the unsupported media type error?, is that related to that the object i send is not what the api method expects?
Hope someone can help.
You need to set the Content-Type header when sending the request:
sendUpdatdReservation(updatedReservation: Reservation) {
var result;
var objectToSend = JSON.stringify(updatedReservation);
var headers = new Headers();
headers.append('Content-Type', 'application/json');
this.http.post('http://localhost:52262/api/postbookings', objectToSend, { headers: headers })
.map((res: Response) => res.json()).subscribe(res => {
this.result = res;
console.log(this.result);
});
}
Don't forget to import this class:
import {Http,Headers} from 'angular2/http';
Does anyone have an example of an API response being passed back from a http.request() made to a 3rd party back to my clientSever and written out to a clients browser?
I keep getting stuck in what I'm sure is simple logic. I'm using express from reading the docs it doesn't seem to supply an abstraction for this.
Thanks
Note that the answer here is a little out of date-- You'll get a deprecated warning. The 2013 equivalent might be:
app.get('/log/goal', function(req, res){
var options = {
host : 'www.example.com',
path : '/api/action/param1/value1/param2/value2',
port : 80,
method : 'GET'
}
var request = http.request(options, function(response){
var body = ""
response.on('data', function(data) {
body += data;
});
response.on('end', function() {
res.send(JSON.parse(body));
});
});
request.on('error', function(e) {
console.log('Problem with request: ' + e.message);
});
request.end();
});
I would also recommend the request module if you're going to be writing a lot of these. It'll save you a lot of keystrokes in the long run!
Here is a quick example of accessing an external API in an express get function:
app.get('/log/goal', function(req, res){
//Setup your client
var client = http.createClient(80, 'http://[put the base url to the api here]');
//Setup the request by passing the parameters in the URL (REST API)
var request = client.request('GET', '/api/action/param1/value1/param2/value2', {"host":"[put base url here again]"});
request.addListener("response", function(response) { //Add listener to watch for the response
var body = "";
response.addListener("data", function(data) { //Add listener for the actual data
body += data; //Append all data coming from api to the body variable
});
response.addListener("end", function() { //When the response ends, do what you will with the data
var response = JSON.parse(body); //In this example, I am parsing a JSON response
});
});
request.end();
res.send(response); //Print the response to the screen
});
Hope that helps!
This example looks pretty similar to what you are trying to achieve (pure Node.js, no express):
http://blog.tredix.com/2011/03/partly-cloudy-nodejs-and-ifs.html
HTH