I am using nodeJS to create an application to create simplified experience of our college's student portal which uses session based authentication. I am aware that in python, we have requests module, from which, we can use requests.Session() object to make get, post etc. requests from the same session. What is the NodeJS equivalent of this?
Also, the portal is set to end the session after 15 mins of inactivity. Is there something I can do to avoid this i.e. generate a never ending session?
Got the solution for this.
I used axios to make requests.
const axios = require("axios");
const axiosCookieJarSupport = require("axios-cookiejar-support").default;
const tough = require("tough-cookie");
axiosCookieJarSupport(axios);
const cookieJar = new tough.CookieJar();
axios.get(url, {
jar: cookieJar,
withCredentials: true,
}).then((res)=>{
//do your stuff
//any set-cookie headers will be automatically processed.
//use the same config for further requests,
//it will automatically send the cookies alongside the requests.
})
Related
I'm trying to work with the eBay APIs. It's a small personal project that just needs to run locally, and although I know C#, I'm much more comfortable with Javascript so I'm looking for ways to get this done in JS.
I found this promising looking eBay Node API with browser support. Browser support is something I'm looking for, but it also says that
A Proxy server is required to use the API in the Browser.
They give an example file for a proxy server that is a Cloudflare worker.
I'm trying to translate that into something I can run in Node locally using the basic Node HTTP server. I've been following through it and am doing OK so far, figured out the different ways to access the headers and check them, etc., but now I'm at the point where the proxy server is making the proxy request to the eBay APIs. The way the example file is set up, it seems as though the Cloudflare worker intercepts the HTTP request and by default treats it as a Fetch Request. So then when it goes to pass on the request, it just kind of clones it (but is replacing the headers with "cleaned" headers):
// "recHeaders" is an object that is _most_ of the original
// request headers, with a few cleaned out, and "fetchUrl"
// is the true intended URL to query at eBay
const newReq = new Request(event.request, {
"headers": recHeaders
});
const response = await fetch(encodeURI(fetchUrl), newReq);
The problem is that I don't have a Fetch Request to clone - since I'm running a Node HTTP server, what is event.request in the example code is for me a http.IncomingMessage.
So how can I turn that into a Fetch Request? I'm guessing at the very least there's stuff in the message body that needs to get passed along, if not other properties I'm not even aware of...
I don't mind doing the work, i.e. reading the stream to pull out the body, and then putting that into the Request object somehow (or do I even need to do that? Can I just pipe the stream from the IncomingMessage directly into a Request somehow?), but what else besides the body do I need to make sure I get from the IncomingMessage to put into the Request?
How do I turn a Node http.IncomingMessage into a Fetch Request and be sure to include all relevant parts?
I've made a simple function to convert.
const convertIncomingMessageToRequest = (req: ExpressRequest): Request => {
var headers = new Headers();
for (var key in req.headers) {
if (req.headers[key]) headers.append(key, req.headers[key] as string);
}
let request = new Request(req.url, {
method: req.method,
body: req.method === 'POST' ? req.body : null,
headers,
})
return request
}
I am using ngrok to expose my localhost on a raspberry pi, and ngrok is password protected. I am using Django on another computer outside my network to access a webpage(simple server) on this raspberry pi. I don't want to manually type in the username and password to ngrok.
How can I automatically fill in the ngrok user/password, which is a popup in chrome asking for user and password?
What I've tried:
I first tried using JS in my template, just using a fetch request:
https://user:password#myngrokdomain.ngrok.io but chrome threw an error in the console saying I can't pass in credentials in plain text like this, rightfully so...
I then tried to use python requests:
UN= "myuser"
PWD = "mypassword"
loginURL = "https://myngrokdomain.ngrok.io"
client = requests.session()
login_data = dict(username=UN, password=PWD,)
r = client.post(loginURL, data=login_data)
This returned a 401 access denied
r.headers + r.reason returns:
401 Unauthorized Unauthorized {'Content-Length': '16', 'Content-Type': 'text/plain', 'Www-Authenticate': 'Basic realm="ngrok"', 'Date': 'Tue, 16 Mar 2021 15:22:15 GMT'}
The authentication method used by ngrok is called HTTP Basic Auth. To use it with the requests library you pass login and password as a tuple in the auth argument:
r = client.post(loginURL, auth=(UN, PWD))
Docs: Basic Authentication — Requests
Try doing a get on the login page first. Perhaps it's setting some cookies that it expects to be present on the post :
UN= "myuser"
PWD = "mypassword"
loginURL = "https://myngrokdomain.ngrok.io"
client = requests.session()
login_data = dict(username=UN, password=PWD,)
client.get(loginURL )
r = client.post(loginURL, data=login_data)
You need to distinguish whether you're making the request from a browser or from a server application.
Browser
If you're doing it from a browser you're hitting a CORS issue and ngrok doesn't support that when providing --auth. From the docs:
you cannot use ngrok's -auth option. ngrok's http tunnels allow you to specify basic authentication credentials to protect your tunnels. However, ngrok enforces this policy on all requests, including the preflight OPTIONS requests that are required by the CORS spec. In this case, your application must implement its own basic authentication
In this case, your only option is to implement authentication in your application instead of using ngrok's --auth.
Server
If you're sending the request from a server application you won't get into any CORS issue but you need to provide the Basic Authentication credentials properly.
Say you have your application exposed via ngrok at http://myapp.ngrok.io protected via --auth user:pass.
In plain Node.js you would do something like this:
const http = require('http')
http.get('http://myapp.ngrok.io', { auth: 'user:pass' }, res => {
const chunks = []
res.on('data', chunk => {
chunks.push(chunk)
})
res.on('end', () => {
console.log(Buffer.concat(chunks).toString('utf-8'))
})
})
Note that to hit the https url you would use Node's https module instead of http, or a higher level library that handles that for you, like node-fetch.
In Python you can do something similar and this question will probably get you on the right path.
I'm working on a full stack project where users can create account, visit their profile, create blogs, read their blogs, delete their blogs etc. In order to perform all these tasks (except signup and login) the user has to be authenticated.
I'm done with the back end but i don't understand how do i send jsonwebtoken from the client side to the server side (i know how to send it from the server side). I know how to get tokens from the server and store them in browser's locaStorage but i don't know how to send them back to the server when i'm making request for reading blogs or deleting blogs or visiting back to my profile after reading all my blogs.
If i do this -
window.location.href = "/blogs";
then i won't be able to send authentication token or i should say i don't know how to send authentication token using this approach.
Here on stack overflow i read about this technique-
window.location.href = "/blogs?token=";
but i don't think developers uses this technique in their projects because as far as i know tokens are supposed to be sent through headers.
If i summarize my question i just want to know how do i send authentication token to the server as well as change the page for different routes for example a different page that shows all my blogs and another page that shows only my profile. If someone else who is not authenticated tries to visit profile route or blogs route, would get a 401 error.
It would be a great help if anyone could solve my confusion or suggest me a book or an article that solves my confusion.
I will try to make it simple. As an example, I will use code from one of my project.
First, you do not explain how you check and validate token on server-side. So to make explication more complete, I will provide some code.
On the server-side, I use a simple function to check each request received and depending on verification and validation process, I will update the request received before sending it to resolver.
NB: current code used Express
In my example, I store the token inside the request header Authorization field.
const isAuth = async (req, res, next) => {
const authHeader = req.get('Authorization');
// if is no authorization field in header, we call
if (!authHeader) {
req.isAuth = false;
return next();
}
const token = authHeader.split(' ')[1]; // `Bearer XXXXXXXXXXXXXXXXXXXXX...`
if (!token) {
req.isAuth = false;
return next();
}
// Here i decode the token
const decodedToken = jwt.verify(token, 'SomeSecretWord');
req.isAuth = true;
return next();
}
On each request received, we check if the header contain an authorization token, if yes, we validate and verify token. If validation is successfully completed, I update isAuth field inside request and set it to true.
app.use(isAuth);
Now you will be able to access the isAuth inside resolvers and return data based on its value (example: throw error if false);
So now, for the client-side, since we expect token to be store inside the headers Authorization field, we need to set it before sending request.
Be sure to already have the token save on client-side.
In my case, user need to login to receive a new token so he store the newly created token inside client-side storage.
Now before sending each request, access token from storage and updare request header with it.
const headers = {
Authorization: "Bearer XXXXXXXXXXXXXXXXXXXXXX",
};
const reqInit = {
method: 'GET',
headers: headers,
};
// send request using fetch
fetch('/someLocation', reqInit)
...
The problem I faced here, was to store the token between requests for a user session.
the easiest and secure way is to save it in the local or session cache (according to google after a small research) and access it on each request.
While creating the json web token on server-side you can specify a expirery so if token was not used for a certain time, it will be invalid and user will need to reauthenticating to receive an other token and save it in his client-side storage.
After some research, I decide to rewrite my backend with graphql (apollo-server
/ express) for server-side and apollo-client for client-side.
since apollo-client provides a library to manage local cache on client-side, it simplifies the task.
I hope I have answered your question and that can help you and sorry if I made a mistakes.
Add authorization header to your request
headers: {
"authorization": "Bearer your_token"
}
Its and example for adding header to ajax request.
There were some features I wanted from apollo-server and spent some time refactoring my code to it (was previously using express-graphql). The only problem now is a "CORS" problem between my web app (using apollo-client) for authenticated requests. I recall having this problem as well back in version 1.x, and spent a lot of time wrestling with it, but my previous solution does not work in 2.x and would greatly appreciate some help for anyone who has managed to get this to work.
my webapp is hosted on localhost:8081
my server is hosted on localhost:4000
Following their code verbatim from here, I have the following:
Client code
const client = new ApolloClient({
link: createHttpLink({
uri: 'http://localhost:4000/graphql',
credentials: 'include'
})
})
Server code
// Initializing express app code
var app = ....
// Using cors
app.use(cors({
credentials: true,
origin: 'http://localhost:8081',
}));
// Apollo Server create and apply middleware
var apolloServer = new ApolloServer({ ... });
apolloServer.applyMiddleware({ app })
When the client queries the backend via. apollo-client, I get the following error in Chrome:
Yet in my server code, I am explicitly setting the origin. When I inspect the network request in console, I get a contradictory story as well as Access-Control-Allow-Origin explicitly is set to http://localhost:8081
I don't encounter this problem when using Insomnia to query my backend server and get the expected results. Does anyone have any experience setting up apollo on client and server on localhost and successfully authenticating?
I'm actually just dumb. I was looking at the wrong response, sideshowbarker was onto it, apollo-server-v2 automatically overrides CORS and sets the Access-Control-Allow-Origin header to * by default. Even if your express app specifies CORS and you apply that as a middleware to the apollo-server, this setting will be overridden for GraphQL routes. If anyone faces this problem, hopefully this'll save some time:
https://github.com/apollographql/apollo-server/issues/1142
You can now specify CORS explicitly for apollo-server in its applyMiddleware method:
https://www.apollographql.com/docs/apollo-server/api/apollo-server.html#Parameters-2
I have implemented JWT authentication using Node.js. When the user signs in, Node.js signs/creates a JWT and sends it back. Thereafter, it is stored in the localStorage. Now, this is probably where I am going wrong, but... to move forward, I make use of the express router, and within the router code (which is obviously at the node level) I want to be able to access the token (which is in localStorage) so that I can make a call to the API for further data. However, I just realised that localStorage is at the client-end and that node/express/router doesn't recognise localStorage. So I am stuck. Obviously, I am doing something fundamentally wrong... I should not need to access localStorage from the express router file. Perhaps, I should really be making the API calls not from the express router file, but from client side.
Any hints/directions?
localstorage is bad way to save token. you should save token in cookies and use then where you want.
EXAMPLE:
new Cookies(req,res).set('access_token',token,{
httpOnly: true,
secure: true // for your production environment
});
and then read:
var token = new Cookies(req,res).get('access_token');
You need to send the JWT that is stored on the client side every time you make an API request to the server side.
https://jwt.io/introduction/
Scroll down to the section How do JSON Web Tokens work? The JWT should be sent in the header of the API calls in the form:
Authorization: Bearer <token>
How you do this depends on how exactly you'll send the HTTP requests to the API, but it should be pretty simple in any respects. You can find out about how to add Headers to an angular $http request at this link:
https://docs.angularjs.org/api/ng/service/$http
Then it's up for each of your authenticated express routes to check the headers, pull the JWT out, ensure that it's valid, and then proceed with the request (or halt it if the JWT is invalid).