Retrieve Gmail access_token - google-chrome-extension

I need to find the google access_token that is sent to the browser after user logs in to GMail. It should be somewhere among cookies or in a browser local storage.
The problem is that the
chrome.identity.launchWebAuthFlow({url: authURL, interactive: true}, cb)
wants from the user to choose the account again even when he is already logged in to GMail.
What I really want is something similar to Microsoft Outlook Office.context.mailbox.getCallbackTokenAsync that allows to the logged user using Microsoft REST API without authenticating again.
P.S. GMail allows the user to be simultaneously logged in to multiple google accounts where each browser tab may show different active mailbox.
P.P.S. It looks like the well known issue that can be googled:
https://jindalsachin.wordpress.com/category/eathical-hacking/eathical-hacking-stuff/steal-gmail-cookiecookie-stealing-part-2/
I don't find the GX cookie in Dev console, may be it is not relevant anymore.
Instead of it the cookie that the most looks like the one is - 'COMPASS', it belongs to google domain, secured and http-only.
I tried to run this code in gmail context but still unsuccessfully:
let actualCode = '(' + function() {
let access_token = '[COMPASS cookie]';
window.fetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', {
credentials: "include"
}).then(res => {
return res.text().then(dt => ({text: dt, status: res.status}));
}).then(res => {
console.log(res);
});
window.fetch('https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=' + access_token, {
}).then(res => {
return res.text().then(dt => ({text: dt, status: res.status}));
}).then(res => {
console.log(res);
});
} + ')();';
let script = document.createElement('script');
script.textContent = actualCode;
(document.head||document.documentElement).appendChild(script);
I get 401:Invalid Credentials in both cases.

Follow the steps in this quick start and you will have an example of what you are looking for I think. https://developers.google.com/gmail/api/quickstart/python

Related

I'm having issues using the credential manager through chrome extension

I am trying to integrate the credential manager API into my extension so I can save the API credentials using it but am having issues.
Just for testing purposes, I just tried saving the credentials from the popup (Where the user login happens) and when I tried that I got an error saying "NotSupportedError: The user agent does not support public key credentials." I did some googling but didn't find what I was looking for. I thought it might have something to do with the popup so I tried doing the same through the background script but it can't access the window object so that didn't work.
I haven't yet tried doing this though a content script which I would imagine would work but ideally I'd prefer to just store it when the user logs in and then just call it as I need it.
Here is the basic code I'm using for testing that I grabbed from the MDN site;
if ("PasswordCredential" in window) {
let cmAPICredential = new PasswordCredential({
id: "extension",
name: uname,
password: pwd
});
navigator.credentials.store(cmAPICredential).then(() => {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Credentials stored in the user agent's credential manager.`, cpage: "main.js" }, function(response) {});
}
}, (err) => {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Error while storing the credential: ${err}`, cpage: "main.js" }, function(response) {});
}
});
} else {
if (isDebugMode) {
chrome.runtime.sendMessage({ message: "debug_log", str: `**LOGIN DEBUGGING** | Password credential isn't supported in this browser`, cpage: "main.js" }, function(response) {});
}
}
Is there any way that I can do what I am attempting? The only thing I really want to secure is the password and this seems to be the only good way to do it through a chrome extension.
Based on #wOxxOm response and some other research, it looks like I can't use the credential management API with chrome extensions.
So the answer to my question is you can't.

How to query the gitlab API from the browser?

Just to give some context, I'd like to implement a blog with gitlab pages, so I want to use snippets to store articles and comments. The issue is that querying the API from the browser triggers a CORS error. Here is the infamous code:
const postJson = function(url, body) {
const client = new XMLHttpRequest();
client.open('POST', url);
client.setRequestHeader('Content-Type', 'application/json');
return new Promise((resolve, reject) => {
client.onreadystatechange = () => {
if (client.readyState === 4) {
client.status === 200
? resolve(client.responseText)
: reject({status: client.status, message: client.statusText, response: client.responseText})
}
}
client.send(body)
})
};
postJson('https://gitlab.com/api/graphql', `query {
project(fullPath: "Boiethios/test") {
snippets {
nodes {
title
blob {
rawPath
}
}
}
}
}`).then(console.log, console.error);
That makes perfect sense, because it would allow to fraudulently use the user's session.
There are several options:
Ideally, I would like to have an option to disable all form of authentication (particularly the session), so I could only access the information that is public for everybody.
I could use a personal access token, but I'm not comfortable with this, because the scopes are not fine-grained at all, and leaking such a PAT would allow everybody to see everything in my account. (doesn't work)
I could use OAuth2 to ask for every reader the authorization to access their gitlab account, but nobody wants to authenticate to read something.
I could create a dummy account, and then create a PAT. That's the best IMO, but that adds some unnecessary complexity. (doesn't work)
What is to correct way to query the gitlab API from the browser?
After some research, I have found this way to get the articles and the comments. The CORS policy was triggered because of the POST request with a JSON content. A mere GET request does not have this restriction.
I could recover the information in 2 times:
I created a dummy account, so that I could have a token to query the API for my public information only,
Then I used the API V4 instead of the GraphQL one:
// Gets the snippets information:
fetch('https://gitlab.com/api/v4/projects/7835068/snippets?private_token=AmPeG6zykNxh1etM-hN3')
.then(response => response.json())
.then(console.log);
// Gets the comments of a snippet:
fetch('https://gitlab.com/api/v4/projects/7835068/snippets/1742788/discussions?private_token=AmPeG6zykNxh1etM-hN3')
.then(response => response.json())
.then(console.log);

Implement Log In With Spotify Popup

Hi I want to implement a Log In with Spotify feature in my website but I don't want to redirect users to a different page, I would like to just open a popup window. An example of the behavior I want is found at https://developer.spotify.com. There when you click on log in, a pop up window is opened so you can log in with spotify without any redirect.
That's how Spotify Developer website does it:
Open a popup window to /api/authorize. Once the user has allowed the application, it will redirect him to the callback page.
On the callback page, use the returned authorization code (GET parameter code) to generate access/refresh tokens by doing a POST request to /api/token (check out the documentation). This should be done on server side because it requires sending client ID and client secret keys.
Store the access/refresh tokens in the localStorage and close the popup.
Detect close event, get the tokens from the localStorage and use them for the API.
Example
Login page:
// Open the auth popup
var spotifyLoginWindow = window.open('https://accounts.spotify.com/authorize?client_id=REPLACE_ME&redirect_uri=REPLACE_ME&response_type=code');
// Close event
spotifyLoginWindow.onbeforeunload = function() {
var accessToken = localStorage.getItem('sp-accessToken');
var refreshToken = localStorage.getItem('sp-refreshToken');
// use the code to get an access token (as described in the documentation)
};
Callback page:
// Assuming here that the server has called /api/token
// and has rendered the access/refresh tokens in the document
var accessToken = "xxx";
var refreshToken = "xxx";
/////////////////////////
// Store the tokens
localStorage.setItem("sp-accessToken", accessToken);
localStorage.setItem("sp-refreshToken", refreshToken);
// Close the popup
window.close();
Following up on Teh's response above. If you don't want to use localStorage, I registered a global window function and simply passed the token as a payload back to parent window. Works well for a pass-through experience like saving playlists.
Popup:
popup = window.open(
AUTHORIZATION_URL,
'Login with Spotify',
'width=800,height=600'
)
Callback Function:
window.spotifyCallback = (payload) => {
popup.close()
fetch('https://api.spotify.com/v1/me', {
headers: {
'Authorization': `Bearer ${payload}`
}
}).then(response => {
return response.json()
}).then(data => {
// do something with data
})
}
Callback Page:
token = window.location.hash.substr(1).split('&')[0].split("=")[1]
if (token) {
window.opener.spotifyCallback(token)
}
I wrote about this technique in more detail on Medium.

Cloud Functions for Firebase - action on email verified

Im trying to create a Cloud Function trigger that will execute after email has been verified.
In the Cloud Functions samples I could only find examples on triggers for onCreate and onDelete.
Within the documentation I found something about creating custom action handlers but I don't actually want to replace the standard email verification dialog they have by default, I just want to change the property of a "user" after the email is verified.
Does anyone have any experience with this, and is this even possible? Or is my only option to create my custom verification view/dialog webpage?
I faced this problem and took me a long time to figure it out how to solve so I hope this could help anyone that could get stuck into this too:
1 -> I created a function that was triggered with onCreate() for a new user
exports.sendConfirmationEmail = functions.auth.user()
.onCreate((user) => {
const actionCodeSettings = {
url: 'https://appNextURL.com/',
handleCodeInApp: false//ensure that the link will open into browser
};
return admin.auth().generateEmailVerificationLink(user.email, actionCodeSettings)
.then(async (link) => {
await db.collection('users').doc(user.uid).set({
verificationLink: link,
emailVerified: false
}, {merge: true});
return sendCustomVerificationEmail(user.email, user.displayName, link);
})
.catch((err) => {
console.error("Error:", err);
return Promise.reject(err);
});
});
The generateEmailVErificationLink() will generate the link based on the link we will save on step 3.
The function sendCustomVerificationEmail() is just an internal function that overcomes the standard email firebase send
2 -> Then I created a function that will receive a manual http trigger with the data that would be generated automatically by firebase when sending an automatic email
exports.verifyEmail = functions.https.onRequest((req, res) => {
const {mode, oobCode, apiKey, continueUrl, lang} = req.query;
const link = "https://us-central1-projectId.cloudfunctions.net/verifyEmail/?mode=" + encodeURIComponent(mode) + "&oobCode=" + encodeURIComponent(oobCode) + "&apiKey=" + encodeURIComponent(apiKey) + "&continueUrl=" + encodeURIComponent(continueUrl) + "&lang=" + encodeURIComponent(lang);
return db.collection("users")
.where("verificationLink", "==", link)
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (user) {
const userData: UserData = user.data();
console.log("email verified: ", userData.userId);
return admin.auth().updateUser(userData.userId, {
emailVerified: true
}).then(function (userRecord) {
return db.collection('users').doc(userData.userId).set({emailVerified: true}, {merge: true});
});
});
return res.sendStatus(200).end();
}).catch(function (err) {
console.log("error:", err);
return res.sendStatus(403).end();
});
});
As I saved the link in the onCreate() I can now query that link to get who is the user that I am authenticating
3 -> the third step is to change the link in to Firebase Authentication template to the link generated into the 2nd step:
Navigate to Authentication>Templates:
Click on edit icon> Click on customize action URL:
Navigation
Paste the link generated into the step 2 and save:
Save link
Now every link generated automatically will go trought that function you created on step 2 and you will be able to handle the actions you want to happen.
I hope I could be clear.
you could still check for the verification status (at least) on Android with interface UserInfo method isEmailVerified(); eg. in order to send another verification email upon successful login, in case the current user has not yet verified the email address - and show the login screen again. one could as well HTTP trigger a cloud function or update values in the Firebase directly, through the client library. this might also apply to other platform clients, where one can check for the verification status.
this would not be exactly the event when the email just had been verified, but upon each single login attempt one knows the verification status and this value might be merely relevant on the client-side.
Create a publish button so your users trigger your cloud function
Instead of firing the cloud function immediately upon auth.emailVerified, I'm giving my users a 'Publish Profile' button which fires an http cloud function (passing in user.uid). This function looks up the user auth using the passed in user.uid
if user.uid && auth.emailVerified
write auth.emailVerified to each user.post
By default, post document "post.emailVerified" fields start out false, and cannot be written to except via adminFirestore in a cloud function.

Facebook API Nodejs

I'm trying post a message as admin of a facebook page, but my code keeps posting as my personal account to the "wall" of the page, not as the page itself.
When authorizing my application I ask for the right permissionscopes, and accept them. Although, when I check my permissions with /{USER-ID}/permissions, it says that I've denied the application to manage my pages. Any ideas why?
Here's my code for posting:
var parameters = {};
parameters.message = "Hello world, this is dog.";
parameters.access_token = req.session.access_token;
FB.api('{PAGE-ID}/feed', 'post', parameters, function (result) {
if (!result) {
return res.send(500, 'error');
} else if (result.error) {
if (result.error.type == 'OAuthException') {
result.redirectUri = FB.getLoginUrl({ scope: 'publish_actions,manage_pages,publish_pages', state: encodeURIComponent(JSON.stringify(parameters)) });
}
console.log(result);
return res.send(500, result);
}
res.send(result);
});
And yes, of course I use my page id instead of the placeholder in my code above.
*Update: It seems like the permissions aren't even asked for when authorizing the application. I added the email scope to the list and it appears when I re-authorize the application.
Could this have anything to do with my application not beeing approved?
"Submit for Login Review
Some of the permissions below have not been approved for use by Facebook.
Submit for review now or read more."

Resources