Cookie not being set from node typescript request - node.js

I'm trying to set a cookie in a node request. I have tried using packages like js-cookie, cookie-js, cookie and cookie-manager but none work.
The way I have tried it is very straight-forward, whenever my endpoint gets called i.e. https://develop.api/sess/init, I set the cookie at the very beggining of the endpoint with the following code
import * as Cookies from 'js-cookie';
export const init = async (event: APIGatewayEvent, context: Context) => {
...
Cookies.set('hello', 'hello');
...
}
As my endpoint has an auth header, I can not directly call it into my browser URL due to missing permissions, so I tried generating the fetch function with postman and pasting it into my browser's console. The function is the following
var myHeaders = new Headers();
myHeaders.append("Referer", "accepted.referer.com");
myHeaders.append("key", "somekey");
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch("https://develop.api/sess/init", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
Once called, my request successfully returns the expected response, but it never shows a Set-Cookie header in the network section, neither shows my cookie in the Application section.
I have to mention that I also tried looking for the cookie when making the call within Postman, but it never sets it neither.
Also, I have tried starting the application in localhost, and I have a successful response, but my cookie is still not being set.
About the package showed in the code, I said I have tried it with different ones and their implementations, so I don't think a broken package is the problem.
I'm starting to think that I have a wrong idea about how cookies work, or that someway I am completely blocking the sending of cookies within my code.
Environment
If it helps in any way, my endpoint is being hosted in a AWS Lambda application.
I know this should be trivial, but being battling with it for a day now.

I finally answered my own issue. The key here is that I'm using AWS lambdas as the proxy, therefore, the headers I were using to send the cookies were wrong, I was sending the cookies with the endpoint instead of within the lambda. Let me explain myself.
I was adding 'Set-Cookie':'cookieKey:cookieVal' in the headers of the Postman Call that I was using to test both my local and develop environments.
Instead of that, I needed to send the request within the response of the lambda for the cookies to be registered.
Please check at the following links for similar cases ->
https://aws.amazon.com/blogs/compute/simply-serverless-using-aws-lambda-to-expose-custom-cookies-with-api-gateway/
https://forum.serverless.com/t/how-to-send-a-cookie-as-a-response/1312/7

Related

How can i pass a secure/sensitive string from my back (nodejs) to my front (angular) hidden from user?

I am currently working on a project composed by:
Front: Angular ; Back: NodeJS
The backend compiles angular static files with the command: response.sendFile(path.join(__dirname, 'dirname', 'index.html'));
I have stored my API keys into my backend using dotenv. Then, when i start (in main.ts file) my Angular app, i do an API call to my back to get these API keys. But the response is visible from user POV, so i encrypted it. But now, I have to hide the encryption key. That's the string I want to pass from my back to my front.
Anyone has an idea ?
There is no way to hide such information from a very experienced and determined user / attacker. All types of obfuscation can be broken because your code for de-obfuscation / decryption will run at the client side and can thus be re-enigeered, as I already stated in my comment.
To be safe you should build a proxy layer for the unprotectable keys (SendInBlue, Stripe). The proxy can be built quite lightweight by using the same routes as the actual APIs extended by some prefix like, e.g. /sendinblue. This way you can use a single request handler and just add the authorization information (code not tested, just a hint):
const axios = require('axios');
app.get('/sendinblue/:remainder', (request, response, next) => {
// I am not sure, if this takes the whole remainder or just until
// the next slash /
// If not you have to parse the url-attribute of the request object
let originalurl = request.params.remainder;
axios.get(sendinblueBaseUrl + '/' + originalurl, {
headers: {
'Authorization': // API key here
}
})
.then(result => {
response.send(result);
});
});
The code can be reduced even more by using axios interceptors.
I know, that is not satisfying but frustrating. Nonetheless, you should not try to implement some amateurish (no offense!) encryption or obfuscation.

How do we send post request with Kerberos authentication with axios on linux?

we are trying to call POST api to generate certificate for renewal of certification. However as per the 3rd party API reqirement, we need to validate Kerberos authentication. We have tried many thhings with no luck.
We are getting 401 unauthorized error when we are trying to run it.
Tech stack: Node.js, Javascript, Kerberos auth, Linux OS.
Code snippet:
const axios = require('axios');
const data = {
Request: "-----BEGIN CERTIFICATE REQUEST-----<csr key>-----END CERTIFICATE REQUEST-----",
CertificateTemplateName: "DBAPI1Y",
PrimaryEmail: "test#test.com"
};
axios.post('http://dummyurl.com/webapi/SubmitRequest', data, "Negotiate")
.then((res) => {
console.log(`Status: ${res.status}`);
console.log('Body: ', res.data);
}).catch((err) => {
console.error(err);
});
Tried this approach with no luck: How do I use Negotiate or Kerberos authentication with axios?
Can someone please help?
I was unable to find any traces of Negotiate support within axios. Your linked post says that in browsers it would work automatically, which is indeed true for any in-browser JavaScript (that uses Fetch or XMLHTTPRequest behind the scenes) – but the node CLI is a different world. When run through Node CLI, axios will use the HTTP client provided by Node, which doesn't do Kerberos.
It seems that you will need to implement this manually, by using the krb5 module to get a token, then telling axios to send it in the Authorization header – similar to Bearer tokens.
The following example seems to kind of work, although not very pretty – it cannot cope with '30x' redirects:
const axios = require("axios");
const krb5 = require("krb5");
// The service is "HTTP" (uppercase), regardless of the URL being http or https.
token = await krb5.spnego({hostbased_service: "HTTP#dummyurl.com"});
resp = await axios.post("https://dummyurl.com/webapi/SubmitRequest",
data,
{
headers: {
"Authorization": `Negotiate ${token}`,
},
// SPNEGO tokens are single-use, so if Axios tries to
// follow redirects, the 2nd request will be rejected
// as a "replay". So tell it to not even try.
maxRedirects: 0,
});
(I have no experience with writing "proper" Node.js code, especially async-based code, and while in theory it should be possible to use axios' transformRequest to dynamically get a new token for each request, I was unable to figure out how to do it within a sync function.)
In general, I would probably do the task in Python instead (which has well-maintained Kerberos integration using requests-gssapi or httpx-gssapi for Requests or httpx respectively).
Note that Kerberos doesn't ensure data integrity for HTTP, so you must still use HTTPS with it, otherwise someone could still simply MitM the requests.
Apart from the above approach suggested by #user1686, I have solved the problem using NTLM authentication. Specifically, I have used the httpntlm module.
By using the httpntlm package, we don't need to deal with kerberos packages as all the kerberos packages have many dependencies on node-gyp.
Thus, it's better to use some other solution apart from the kerberos related packages.
Note: This solution worked for me, but it will vary from use case to use case.

Setting a cookie from a previous request when using axios in a Lambda function

I am using Axios in my NodeJs application to do HTTP requests.
I am logging in using a post request that does not require a cookie set in the header.
const instance = axios.create({ baseURL: 'https://some_url.com' , withCredentials: true});
const response = await instance.post('auth/login', data);
This returns a set-cookie in its header that I need to use in all subsequent API call. This is code I have tried for this.
const getResponse = await instance.get('/getStuff?$top=10', { withCredentials: true });
This always returns a "Not logged in error". I do not have access to the server, but I am assuming this is because my get request did not send the cookie in its header.
Running all of this in a lambda, not sure if that makes a difference.
Question: How do I get the cookie from my first post request and use it in my get request?
The withCredentials option is for the browser version of axios, and relies on browser for storing the cookies for your current site.
Since you are using it in Node, you will have to handle the storage yourself.
TL;DR
After the login request, save the cookie somewhere. Before sending other requests, make sure you include that cookie.
To read the cookie, check response.headers object, which should have a set-cookie header (which is all cookies really are - headers with a bit of special convention that has evolved into some sort of standard).
To include the cookie in your HTTP request, set a cookie header.
General example
You could also look for some "cookie-handling" libraries if you need something better than "save this one simple cookie I know I'll be getting".
// 1. Get your axios instance ready
function createAxios() {
const axios = require('axios');
return axios.create({withCredentials: true});
}
const axiosInstance = createAxios();
// 2. Make sure you save the cookie after login.
// I'm using an object so that the reference to the cookie is always the same.
const cookieJar = {
myCookies: undefined,
};
async function login() {
const response = await axiosInstance.post('http://localhost:3003/auth', {});
cookieJar.myCookies = response.headers['set-cookie'];
}
// 3. Add the saved cookie to the request.
async function request() {
// read the cookie and set it in the headers
const response = await axiosInstance.get('http://localhost:3003',
{
headers: {
cookie: cookieJar.myCookies,
},
});
console.log(response.status);
}
login()
.then(() => request());
You could also use axios.defaults to enforce the cookie on all requests once you get it:
async function login() {
const response = await axios.post('http://localhost:3003/auth', {});
axios.defaults.headers.cookie = response.headers['set-cookie']
}
async function request() {
const response = await axios.get('http://localhost:3003');
}
As long as you can guarantee that you call login before request, you will be fine.
You can also explore other axios features, such as interceptors. This may help with keeping all "axios config"-related code in one place (instead of fiddling with defaults in your login function or tweaking cookies in both login and request).
Lambda
AWS Lambda can potentially spawn a new instance for every request it gets, so you might need to pay attention to some instance lifecycle details.
Your options are:
Do Nothing: You don't care about sending a "login request" for every lambda run. It doesn't affect your response time much, and the other api doesn't mind you sending multiple login requests. Also, the other api has no problem with you having potentially multiple simultaneous cookies (e.g. if 10 lambda instances login at the same time).
Cache within lambda instance: You have a single lambda instance that gets used every once in a while, but generally you don't have more than one instance running at any time. You only want to cache the cookie for performance reasons. If multiple lambda instances are running, they will each get a cookie. Beware the other api not allowing multiple logins.
If this is what you need, make sure you put the axios config into a separate module and export a configured instance. It will be cached between runs of that one lambda instance. This option goes well with interceptors usage.
const instance = axios.create({...});
instance.interceptors.response.use(() => {}); /* persist the cookie */
instance.interceptors.request.use(() => {}); /* set the cookie if you have one */
export default instance;
Cache between lambda instances: This is slightly more complicated. You will want to cache the cookie externally. You could store it in a database (key-value store, relational, document-oriented - doesn't matter) or you could try using shared disk space (I believe lambda instances share some directories like /tmp, but not 100% sure).
You might have to handle the case where your lambda gets hit by multiple requests at the same time and they all think they don't have the cookie, so they all attempt to login at the same time. Basically, the usual distributed systems / caching problems.

How to authorize for Amazon's Alexa API?

I want to send a request to this Amazon Alexa API.
That page contains the last 50 activities I made with my Amazon Echo. The page returns JSON. Before you can request that page, you need to authorize your account, so the proper cookies are set in your browser.
If I do something simple as:
const rp = require("request-promise");
const options = {
method: "GET",
uri: "https://alexa.amazon.com/api/activities?startTime=&size=50&offset=-1",
json: true
};
rp(options).then(function(data) {
console.log(data);
}).catch(function(err) {
console.log(err);
});
I can send a GET request to that URL. This works fine, except Amazon has no idea it's me who's sending the request, because I haven't authorized my NodeJS application.
I've successfully copied ~10 cookies from my regular browser into an incognito tab and authorized that way, so I know copying the cookies will work. After adding them all using tough-cookie, it didn't work, unfortunately. I still got redirected to the signin page (according to the error response).
How do I authorize for this API, so I can send my requests?
I have been looking for a solution for this too. The best idea I have is to use account linking, but I haven't try it yet. Looks like ASK-CLI has interface for this also, but I can't figure it out how to use it (what is that URL?). For linking account to 3rd party server is not easy, but link it back to Amazon for the json API should not be that complicated.

Vue.js 2 Authentication JWT

[ Post has been edited: see below for answer ]
I am trying to make a Vue.js2 application using this boilerplate https://github.com/vuejs-templates/webpack
I am stuck on the Authentication process, using this library: https://github.com/websanova/vue-auth and attempting to use JWT as the authentication method. Never having rolled my own authentication before, I am slightly lost.
Packages I have installed which may be relevant: passport, passport-jwt, jsonwebtokens, passport-local, passport-local-mongoose
Looking at my logs I get a successful login response, but then it attempts to get /auth/user and responds with a 401 (unauthorized error). Perusing the auth library code, a GET req to /auth/user seems to be the expected behavior.
Here is the login code (client side):
methods: {
login() {
this.$auth.login({
body: this.data.body
success(res) {
console.log('Success: '+this.context);
this.localStorage.setItem('auth-token', res.body.token);
},
error(res) {
console.log("Error: " + this.context);
this.error = res.data;
},
rememberMe: true,
fetchUser: true
});
}
}
And here is the appropriate code server-side:
Removed Link | See Edits Section *
What I am sure of is this:
the server does in fact create a JWT which is valid (checked on jwt.io) during the login request. It does not appear to be set anywhere afterwards. It just sits there and then dies. There are mentions of an Authorization Bearer header in the response, which I am certain is not being set. Nor do I understand where or how to do this. There is no token set in localStorage after the login request. I'm not sure if this should exist, but think it likely that it should. In my console, searching local storage yields some strings and large integers, but no mention of a token in it.
Edits (8+ months later)
Gist to Solution here (slashes replaced by dashes in filenames):
https://gist.github.com/wcarron27/b0db7a16df9ceff924d4a75050093c55
The reason my login method originally did not work was that the localStorage token was not set correctly, and thus failed to pass the getData method on the client-side redirect. vue-auth does this by default. By editing the url it hits in the vue-auth config, I was able to direct it to the proper route(BUT only after I properly set the localstorage token. Use Vue.http.options.rootUrl (or something, it's in the main.js file in the gist) to set the Authorization header.
In the code, You must register vue-auth on the client side main.js, and call to it in the Login.vue "login" method. The client side config directs the http calls to the specified route in main.js. In the callback, the user and tokens are set in localStorage and the Vuex store.
The Http reqs go the the API side and hit the route in accounts.js. That route uses a passport strategy defined in ./util/passport.js, as well as a setJWT function defined in ./util/jwtLib.js.
After this, the client is redirected to a route of my choice, and data is populated by my store and ajax calls. Keep in mind, that while this should solve logins, i have not verified code correctness, as basically I would have needed to post the whole of two separate codebases.
Additionally, this does not account for refresh errors. State is dropped on refresh, so do not rely on vuex for persistence. A combination of localStorage and vuex does the trick, though.
I didn't verify this but, does remove the 'this' from your code on line 7 do the magic?
methods: {
login() {
this.$auth.login({
body: this.data.body
success(res) {
console.log('Success: '+this.context);
// original code here --> this.localStorage.setItem('auth-token', res.body.token);
localStorage.setItem('auth-token', res.body.token);
},
error(res) {
console.log("Error: " + this.context);
this.error = res.data;
},
rememberMe: true,
fetchUser: true
});
}
}

Resources