For a webExtension I'm trying to check the gmail inboxes by sending a xmlhttprequest to:
https://mail.google.com/mail/u/0/feed/atom
https://mail.google.com/mail/u/1/feed/atom
https://mail.google.com/mail/u/2/feed/atom
... and so on for each account. I want to do this for all currently logged in accounts, but I dont know how to find out that number.
Of course, there is a dumb way, in that I just keep incrementing the u/#/ until I loop back to /u/0/, and do a check there.
But that is slow, since I'd then have to wait for the async to return, hopfully there will be a better way.
Not sure about gmail specifically, but you could use browser.cookies.getAll to read all the user cookies for "mail.google.com" (or accounts.google.com or similar).
This assumes you know the name of the cookie(s) you are looking for, so that you can infer on how many accounts the user is logged in.
Also, make sure to add the cookies permission to your manifest.json
browser.cookies.getAll({ domain: 'mail.google.com' })
.then(cookies => {
// do something with the cookies
});
If instead you can assume that each gmail inbox is open in some tab, then you could just query all the current tabs:
browser.tabs.query({ url: "https://mail.google.com/mail/u/*" })
.then(tabs => {
tabs.forEach(tab => {
// do an XHR request to tab.url or just extract the inbox ID for later use
});
})
Related
Background Information
I am working on a NodeJS w/ Express project.
I am using some auth middleware, we can call it isAuthenticated()
The middleware is just protecting certain routes w/ passport, etc.
I am adding a file download link that is visible when a user is not authenticated yet. (i.e. the button is publicly visible).
If you click the button, the download route is protected, so we redirect you to a sign in page.
The Problem
Once the user authenticates, I am calling res.download(file);. The download works fine, but the user is still at the sign in page with their credentials typed in. (Note: the user is authenticated, but no further redirect is happening on this route). If I try res.download(file) and then make a call to res.redirect('/'), the file isn't downloaded.
Basically: I want to redirect the user to the home page at 'website.com/', but the approaches I've taken haven't worked.
Main Approach:
Set res.local.downloadFile2 = true in the router.get('/download/file2') route. Then redirect to home using res.redirect('/'). Now, in the home route, I would just do the following after rendering the home page again.
if (res.locals.downloadFile2 === true) {
res.download(file2)
}
However: res.locals.downloadFile2 === undefined when that check is being done. I've tried to step through and somewhere it is being reset in one of the _modules I am using, so this is not working. I am currently unsure of the cause.
I could probably solve this by not displaying the links until a user is Authenticated, then there shouldn't be any cases of redirecting to login and back again, which means they would never see the login page, etc. but that is not the right solution to this problem, and is not the one I want to implement.
I would really appreciate any help, and can provide more information if needed!
Thanks in advance.
The issue is that after the redirect, the res object is a complete new object due to the browser issued a new request to follow the redirect. That's why res.locals.downloadFile2 is undefined.
To solve your problem, you can replace your code res.local.downloadFile2 = true with code to set a cookie. The cookie holds information which will be stored on client side by the browser and send to the server with every request.
See cookies npm package for example:
var Cookies = require('cookies');
var cookies = new Cookies(req, res, { keys: ['arbitrary string to encrypt the cookie'] })
// get value of the cookie
var need_to_download_file = cookies.get('need_to_download_file', { signed: true })
// Logic to download file (if necessary)
if (need_to_download_file) {
// Trigger download of the file
// Reset cookie
cookies.set('need_to_download_file', false, { signed: true })
}
// set the cookie
cookies.set('need_to_download_file', true, { signed: true })
I currently have a webapp I'm writing in Node/Vuejs with Passport handling authentication, and I've run into a problem. I was thinking about how I have authentication currently set up and I realized I had a glaring security hole.
In short, I have my Vuex store hitting a local API endpoint /api/me. That endpoint does a simple return of req.user. For the sake of brevity, a typical response looks like this:
{
username: 'Bob',
roles: [] // normal user has no roles,
email: 'someguy#bob.com'
}
My admin route /admin has a beforeEnter check, as shown below, that incorporates this check using the Vuex store, so I can have a cached version of user data accessible on the frontend.
{
path: '/admin',
name: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
store.dispatch('getMe').then(() => {
if (store.getters.user.roles && store.getters.user.roles.includes('administrator')) {
next();
return;
}
next({ path: '/' });
});
}
}
Here's the thing though - I realized that someone could easily game the system. In fact, I tried it myself with a test, non-Adminstrator account, and I was able to get in by returning the following from a local server set up for this purpose in Postman:
{
username: 'Super Admin Joe',
roles: ['administrator'] // normal user has no roles,
email: 'admin#bob.com'
}
And viola! The user now has full access to admin pages.
My question is, how could I prevent against this?
I need to check that the user is authenticated on every page, but a potential attacker could quite easily proxy any request (in this case it's /api/me) to make themselves any user they want. They can login normally with their own account, open the Network tab and copy the response payload, then change the user data as they wish. There needs to be some sort of encryption between the frontend and backend when checking a users' logged-in status, I believe.
I tried thinking about how I could prevent this from happening, but anything on my end (server-side, at least) seems useless as any request could easily be redirected to an attacker's local machine.
Any advice on how to "sign" my requests to make sure they aren't being proxied? Thanks in advance!
You shouldn’t have to be signing the response body of an api request. The typical way to do authentication is to establish a signed session cookie that acts either as an identifier to session information in an external database, or contains session information itself. This cookie should be in the header of your response and passport should give you a way to administer this cookie without you even realizing it.
This way the user can’t tamper with the information sent from the server in a way that’s easy to detect, and since it’s a cookie it will automatically be sent with each request by your browser (although if you’re using some AJAX library you may have to explicitly specify you’d like to send the cookie). What MadEard was referring to in the comment is where the cookie information is able to be accessed using passprt which is the ‘user’ property in the ‘req’ object.
After reading your github files:
server.get("/admin", function(req, res){
if(req.user && req.user.roles.includes("administrator")){
//user is an administrator, render the admin panel view
}else{
//is not an admin, redirect or send error message
}
});
In every Express route, after authentication with Passport, you have the req.user object.
It is established by checking the request cookie connect.sid, and checking which session this cookie belongs to on the server.
As such, you can trust that in any Express route, the object req.user contains the information relevant to that cookie and you can act upon it.
A little note: doing server-side validation should become a reflex for you over time.
The client is meant to display information. If, at any point, you are making the client take any decision that could be a security liability, take a step back and think it again.
I am having a tough time understanding how I'm supposed to implement sessions.
Currently I'm writing a Vuejs app and, somehow, have managed to evade implementing any kind of Oath2 "sign in with x" process, or at least have copy and pasted my way to success without understanding, I'm afraid.
Here's what I'm trying to do, and it actually works 90% of the way there.
I've implemented passport-steam and I can click through a Sign in to Steam button, login with my account, and get kicked back to my homepage. A sessionID cookie is set. This is how I have it all configured:
app.use(session({
secret: 'topSecretKey!',
name: 'sessionID',
resave: true,
saveUninitialized: true
}))
passport.use(new SteamStrategy({
returnURL: `${host}/auth/steam/return`,
realm: host,
profile: true, // enable profile exchange
apiKey: ApiSettings.apiKey
}, (identifier, profile, done) => {
process.nextTick(() => {
// ...
// fetch the user profile in the background, matching
// the steamID to the account
profile.identifier = identifier
return done(null, profile)
})
}
))
app.get('/auth/steam/return', passport.authenticate('steam', {
failureRedirect: '/'
}), (req, res) => {
console.log('sessionID=' + req.sessionID)
console.log(`User logged in w/ Steam ID #${steamID}`)
res.redirect('/')
})
I run all of this, I sign in, and check my Node console and all is well!
Found existing account.
sessionID=2YL_YdMrauNyZ-F0gnIv3XV_5sNFo4C9
User logged in w/ Steam ID #xxxxxx
But here begins my questions. sessionID is stored in the request object, this is clear.
Is the sessionID supposed to be used as a token when querying an OpenID-enabled API? (Steam API doesn't require anything other than the Steam ID which is returned plain as day.
Is the sessionID supposed to ever make it to the frontend? Currently I static-serve a /dist directory (again, this is Vue) so Node doesn't actually do much except handle API calls out to the SteamAPI. Of course, this doesn't actually require a sessionID... just an APIkey and the users SteamID which I store in their user profile on the Mongo side.
What is the pattern here? Am I supposed to put the users' sessionID in their user record in Mongo and ... pass it around? Seems strange to me to do it that way.
What's the procedure to check if a user is logged in? Check the cookie sessionID and make a request to return that user object to the frontend (using Vuex layer for quick retrieval).
An extension of #2, but in theory, is the sessionID ever supposed to make it to the frontend? The only data manipulation is happening in my Vuex stores - for example, adding a game for a user. How is the sessionID supposed to be used?
Thanks in advance - I know these questions are all over the place but I've been pulling my hair out!
UPDATE August 2nd:
Thanks to anyone who read - I had some developments last night that were definitely very helpful in understand how this workflow is supposed to go. Couple things I learned:
The sessionID is not supposed to be referenced on the frontend - it is passed back and fourth automatically between frontend and back to Node via the request
Rather than relying on a cookie (which keeps resetting the sessionID every time Node restarts) I set up a Mongo store using connect-mongo. This way my sessionID never actually changes until it naturally expires.
As for #4, checking if a user is logged in, I am just setting a cookie called loggedIn. I honestly can't think of a better way to do this, as the sessionID cookie is HTTP only, and thus, unavailable to any frontend Javascript/Vue in this case.
I've come to the understanding that the sessionID is to be used in the backend only, but I still am wondering if I'm supposed to connect it to the user some way. This link was super helpful but I still feel as if my workflow is not perfect. When I do passport.serializeUser, what am I ideally supposed to pass to done() ? I am passing done(null, user.steamID) as the SteamAPI only requires the user SteamID to request their game list. Is this OK to do, or should I be passing the sessionID? Still confused!
UPDATE AGAIN
Great source of information here, regarding the security and the why behind all of the little details.
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.
Alrigh, so I have set up an entire passport local authorization, every thing works pretty perfectly, except I can't seem to wrap my head around one issue with passport.js.
I am authorizing users through sessions (they work fine), however I can't seem to extract the user info from that session, although if I manually check my session db, for every session there is a userid (which links to the user).
Also the deserialize functions seem to return the user and the log is showing the correct user.
Now the problem is that I can't seem to pass this user<->session info anywhere in my routes.
The issue that I seem to have with this is that people that are logged in, are authorized, but they can fiddle with the request body as much as they like, doing things as posting messages in their name, logging them out, ...
Code example:
router.post('/games/:id/verify', isAuthenticated, function(req, res){
log('#POST verify for game with id: ' + req.params.id);
gameController.postVerify(req.params.id, req, res);
});
In this code, it just checks if a user is logged in, if possible, I want to check which user is logged in to that session, so that he cannot verify this game if he was not part of it.
Let's say user A, B were part of the game and need to verify it. This function allows user C who is logged in to verify for them, because this function only checks if the user is logged in.
If I can pass the logged in user (user C in the test case) to the gameController, I can write my own checks in there and throw them out.
Passport will populate req.user when authentication has succeeded. It will be populated by the data provided by deserializeUser, which generally is a database record of the user.
You can use that information to limit other database queries/checks/etc to make sure that the logged-in user is allowed to access particular information.