How to access params from URL after redirect to different site (Express) - redirect

The SurveyMonkey API (documentation) requires implementation of oauth to let the user decide which scopes of their account the developer has access to.
I have the following code (adapted from this question) as a means to implement the first two steps in their documentation:
app.get('/surveymonkey/oauth', function (req, res) {
const code = req.query.code;
const post_body = querystring.stringify({
"client_secret": <client_secret>,
"redirect_uri": "https://b2e3b137.ngrok.io/surveymonkey/oauth",
"grant_type": "authorization_code",
"code": code
});
const options = {
hostname: 'api.surveymonkey.net',
port: 443,
path: 'oauth/authorize?api_key=<api_key>&redirect_uri=https%3A%2F%2Fb2e3b137.ngrok.io%2Fsurveymonkey%2Foauth&response_type=code&client_id=<client_id>',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(post_body)
}
};
res.redirect('https://api.surveymonkey.net/oauth/authorize?api_key=<api_key>&redirect_uri=https%3A%2F%2Fb2e3b137.ngrok.io%2Fsurveymonkey%2Foauth&response_type=code&client_id=<client_id>');
console.log(req.params);
console.log(req.body);
console.log(req.query);
req.on('error', function(e) {
console.error(e);
});
});
When I fire up an ngrok server (ngrok http 443), everything is going fine, except when I hit the '/surveymonkey/oauth' route and validate the scopes (acting as the user), I get redirected to the route I specified in my SurveyMonkey App console, which contains the short-lived 'code' param that I'm trying to assign to the user, but I cannot get access to the 'code' param in the query string since it's been redirected to a different site.
The problem I'm currently facing looks similar to this and this, and I'm trying to get meaningful data back from req, but as you can see in the comments above, all of the data is undefined.

Related

CORS error: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response

I'm trying to fetch an image resource that's part of a conversation message.
I've tried both FETCH as well as using AXIOS but I'm getting the same error message.
Here's an example of my FETCH request
const token = `${accountSid}:${authToken}`;
const encodedToken = Buffer.from(token).toString('base64');
let response = await fetch('https://mcs.us1.twilio.com/v1/Services/<SERVICE_SID>/Media/<MEDIA_SID>',
{
method:'GET',
headers: {
'Authorization': `Basic ${encodedToken}`,
}
});
let data = await response.json();
console.log(data);
And here's what Axios looked like
let config = {
method: 'get',
crossdomain: true,
url: 'https://mcs.us1.twilio.com/v1/Services/<SERVICE_SID>/Media/<MEDIA_SID>',
headers: {
'Authorization': `Basic ${encodedToken}`,
},
};
try {
const media = await axios(config);
console.dir(media);
} catch(err) {
console.error(err);
}
Both ways are NOT working.
After looking into it more, I found out that Chrome makes a pre-flight request and as part of that requests the allowed headers from the server.
The response that came back was this
as you can see, in the "Response Headers" I don't see the Access-Control-Allow-Headers which should have been set to Authorization
What am I missing here?
I have made sure that my id/password as well as the URL i'm using are fine. In fact, I've ran this request through POSTMAN on my local machine and that returned the results just fine. The issue is ONLY happening when I do it in my code and run it in the browser.
I figured it out.
I don't have to make an http call to get the URL. It can be retrieved by simply
media.getContentTemporaryUrl();

How can I reach the 'Retry-After' response header using axios?

I'm building a simple Vue2 app with Auth section, which makes requests to REST API service.
So, I have my axios instance:
const instance = axios.create({
baseURL: BASE_URL,
timeout: DEFAULT_TIMEOUT,
withCredentials: true,
headers: {
accept: 'application/json',
},
});
To make authorization requests I use a separate module:
const auth = (api) => ({
submitPhoneNumber({ userPhone }) {
return api.get(`auth/${userPhone}`);
},
});
And set it all up together like this:
export default {
auth: auth(instance),
};
Then I add my api to Vue as a plugin:
export default {
install(Vue) {
const vueInstance = Vue;
vueInstance.prototype.$api = api;
},
};
In the component I access my api-plugin and make a request, extracting status and headers from it:
const { status, headers } = await this.$api.auth.submitPhoneNumber({
userPhone: this.userPhone,
});
When I look through the response in chrome devtools, I clearly see a "retry-after" header with number of seconds, after which I can make another request.
Upon receiving the response, I would like to save this number of seconds to some variable and then render a warning message like "Please wait { seconds } to make another submit".
The problem is that in my code I have no such header in the response (while I can see it in devtools, a I said):
see the screenshot
So, when logging the headers from my response, there are just these:
{content-length: '19', content-type: 'application/json; charset=utf-8'}
What is the problem with that?
Try var retrysec = error.response.data.retry_after that worked for me

Token is generated at the endpoint but does not arrive on the page

I want to create a website with Svelte/Kit and use JWT.
I have found instructions on the internet, for example:
Svelte JWT Authentication https://morioh.com/p/1d95522418b2
SvelteKit Session Authentication Using Cookies https://www.youtube.com/watch?v=bG7cxwBMVag
But unfortunately no instructions for Svelte Kit and JWT. So I tried it myself.
The token is generated at the endpoint, but does not arrive on the page (or is not callable). I suspect that some setting in the headers is wrong, but can't figure out what is wrong. This is my highly simplified test environment:
(1) I call the endpoint login.js from the page index.svelte. For testing, I omit checking email and password and send JWT right back. Data arrives, but I don't see the JWT.
(2) The JWT should be sent to another endpoint. What is the best way to do this?
The "page" index.svelte (simplified):
<script>
let email="", password="";
const doLogin = async () => {
const response = await fetch("/auth/login", {
method: 'POST',
headers: {
"Content-Type": "application/json",
},
credentials: 'include',
body: JSON.stringify({
email,
password
})
});
if (response.status == 200) {
const { done, value } =
await response.body.getReader().read();
await console.log("done, value=", done,
JSON.parse(new TextDecoder("utf-8").decode(value)));
await console.log("headers=", response.headers);
}
}
</script>
<h1>Welcome to MyAuth</h1>
<input type=email bind:value={email}/><br/>
<input type=password bind:value={password}/><br/>
<button on:click={doLogin}>Submit</button>
The "endpoint" login.js (simplified):
import jwt from "jsonwebtoken";
export function post(request, context) {
const token = jwt.sign({
data: { text: "test" },
"topsecret",
});
const response = {
status: 200,
headers: {
'content-type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: {
passwordOk: true,
}
};
return response;
}
The console shows:
done, value= false {passwordOk: true}
index.svelte:59 headers= HeadersĀ {}
index.svelte:44 Fetch finished loading: POST "http://localhost:3000/auth/login".
doLogin # index.svelte:44
I think you are mixing up the two major parts to authentication:
Requesting/sending credentials.
Using those credentials to access protected content.
Authorization: Bearer ${token} is normally sent from the (browser) client to the server to request access to protected content. So right now, your server is asking the client for permission. This doesn't make sense.
Instead, the login endpoint should send the token via:
Set-Cookie header in the login endpoint.
The body of the response (where passwordOk is).
Set-Cookie causes the browser to send this value as a cookie with every future request. The server can check for this cookie value before serving protected content. This can be more secure because you can send an HTTP only cookie.
If the token is sent in the body of the response to login the client should send the token in future requests with the Authorization: Bearer ${token} header. The server can then check for this header before serving protected content.

Saml Strategy Verify callback not getting called

As per the passportjs documentation, the verifier function is supposed to be called.
But I am not seeing this call back.
I am testing this with okta. In okta the app is configured to send the saml response to /fromokta
The okta is redirecting to /fromokta
What am I missing?
const saml = require('passport-saml').Strategy;
var strategy = new saml({
entryPoint: "<some oktka entiry point>",
issuer: "http://www.okta.com/foobar",
protocol: "http://"
},(profile, done) => {
console.log(">>>> callback from okta");
console.log(profile);
done(profile)
})
app.post('/login', function(req, res) {
passport.authenticate(
'saml',
{
//successRedirect: '/success',
failureRedirect: '/'
}
)}
)
app.post('/fromokta', function(req, resp){
// this gets called
})
This most likely means your POST /login route never gets the request. Look at the network requests and see if there is indeed a POST /login to the URL, make sure the protocol is the same too (http and https)

Get public feeds of a Facebook Page in Node.js

I'm developing a simple node/express/jade website that fetch all the public feeds of a Facebook Page.
I create an application from wich i get client_id (APP_ID) and client_secret (APP_SECRET).
My code works, and it's okay but i wonder if this is the correct way of handling this need.
Here is the code:
var https = require('https'),
concat = require('concat-stream'),
async = require('async');
function FacebookPage(pageId) {
if (!(this instanceof FacebookPage))
return new FacebookPage(pageId);
this.pageId = pageId;
}
FacebookPage.prototype.getPublicFeeds = function (callback) {
var pageId = this.pageId;
async.waterfall([
function (done) {
var params = {
hostname: 'graph.facebook.com',
port: 443,
path: '/oauth/access_token?client_id=MY_CLIENT_ID&' +
'client_secret=MY_CLIENT_SECRET&grant_type=client_credentials',
method: 'GET'
};
https.get(params, function (response) {
//response is a stream so it is an EventEmitter
response.setEncoding("utf8");
//More compact
response.pipe(concat(function (data) {
done(null, data);
}));
response.on("error", done);
});
},
function (access_token, done) {
var params = {
hostname: 'graph.facebook.com',
port: 443,
path: '/v2.0/' + pageId + '/feed?' + access_token,
method: 'GET'
};
https.get(params, function (response) {
//response is a stream so it is an EventEmitter
response.setEncoding("utf8");
//More compact
response.pipe(concat(function (data) {
callback(null, JSON.parse(data));
}));
response.on("error", callback);
});
}]);
};
module.exports = FacebookPage;
EDIT: thank to #Tobi I can delete the part of getting the access_token by putting access_token=app_id|app_secret as explained here:
Not sure why you'd want to include to OAuth stuff (which I think can't work because you don't exchange the code for an actual access token if I understand this correctly)...
According to https://developers.facebook.com/docs/graph-api/reference/v2.0/page/feed/ you need an access token ... to view publicly shared posts., this means you can also use an app access token in the form of app_id|app_secret.
You can then use the
GET /{page_id}/feed
endpoint by passing the access_token paramenter with your app access token. I'd also recommend to use the NPM modules request or restler, these make the HTTP handling much easier.