Chrome extension get authorization token from header - javascript

I'm working on an extension that will make request to Robinhood API and get all transaction history to generate a better report.
Initial thought, it will be easier to just grab auth token from chrome extension(background.js) and use that to make request to Robinhood API but seems like it's more complicated than I expected.
I can still see the authorization token on the Chrome browser console but can't find the way to grab that from my Chrome extension(pic below)
Thought webRequest API is the answer but found it won't provide Authorization info(https://developer.chrome.com/docs/extensions/reference/webRequest/)
chrome.webRequest.onBeforeSendHeaders.addListener((details) => {
console.log(details);
},
{ urls: ["<all_urls>"] },
["requestHeaders", "extraHeaders"]
);
I know there's Chrome extensions making http requests to Robinhood API with local auth token. What's the best way to grab that local auth token?
Thank you all!

Thank you to #wOxxOm for the answer!
I was missing host_permissions in manifest.json.
The Authorization token info shows up after adding that.

Related

Reactjs Expressjs end to end encryption

I am not sure this question already been asked here. But I could not see such a question so far.
I have a RESTapi written in Express.js.
That RESTapi is connected to a React.js web app.
RESTapi is secured with JWT authentication tokens.
In the web app, we display thousands of product items with daily prices. Someone logging into the web app and viewing few product's prices is okay.
But what is not okay is someone automates the fetch all item's prices daily and store and analytics our pricing strategies.
Basically what I want is for someone trying to access the API using a tool like a postman or something it should be blocked. Only a web browser should be able to access the API. This can be achieved to some extend by blocking the user agents. We can block POSTMON user agents but how we block all the tools like POSTMON?
Even though If we block all the tools like that still browser's dev tools network tab they can see the response.
Is there a way to encrypt the response? So network tab will display something that the users can understand. But at the same time that response can be decrypted by React.
Hope my question is clear to all!
Any help!
Thanks in advance. =)
Basically what I want is for someone trying to access the API using a tool like a postman or something it should be blocked. Only a web browser should be able to access the API.
Can't be done.
This can be achieved to some extend by blocking the user agents. We can block POSTMON user agents but how we block all the tools like POSTMON?
People can lie about the user-agent very easily.
Even though If we block all the tools like that still browser's dev tools network tab they can see the response.
Yes. Browsers are designed to work for their user's benefits above the benefits of the owners of the websites they access.
Is there a way to encrypt the response? So network tab will display something that the users can understand. But at the same time that response can be decrypted by React.
Not really. You'd have to give the decryption key to the browser, and if you give it to the browser then the user can access it.
But what is not okay is someone automates the fetch all item's prices daily and store and analytics our pricing strategies.
Consider rate limiting instead.
Essentially, however, what you are trying to do is make information public without giving it to some people.
That's a contradiction and thus impossible.
what you can do with express is to use CORS and allows only your website to reach the API.
check https://www.npmjs.com/package/cors
you gonna have something like
var express = require('express')
var cors = require('cors')
var app = express()
var corsOptions = {
origin: 'http://example.com',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for only example.com.'})
})
but to use cors for that goal probably need to use a restriction based on user-agent, to make sure that all requests are coming from a browser and with cors header.
But still not a very efficient mechanism.
The other idea is to implement some sort of 'API KEY', generated by your client-side and validated on the backend. each request needs to have an 'API KEY' as param
You need to use CORS policy with HTTPS, if the policy is set correctly in CORS policy the request will be handled only if come from the UI App, otherwise, the app will reject the requests.
hope this code snippet will assist you
const whitelist = ['http://www.example.com', 'https://www.example.com'];
const corsOptions = {
origin(origin, callback) {
if (whitelist.indexOf(origin) !== -1 || !origin) {
// !origin // allow requests with no origin (like mobile apps or curl requests)
callback(null, true);
} else {
console.error(`Not allowed by CORS, Origin ${origin}`);
callback(new Error('Not allowed by CORS'));
}
},
exposedHeaders: 'Authorization',
};
router.use(cors(corsOptions));
router.use('/api/auth', auth);

LinkedIn OAuth redirect login returning "No 'Access-Control-Allow-Origin' header is present on the requested resource" error

I'm currently implementing OAuth login with LinkedIn in my React and Play app and am running into a CORS error when trying to redirect to the authorization page in my dev environment:
XMLHttpRequest cannot load https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_i…basicprofile&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fusers%2Flinkedin. Redirect from 'https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_i…basicprofile&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fusers%2Flinkedin' to 'https://www.linkedin.com/uas/login?session_redirect=%2Foauth%2Fv2%2Flogin-s…' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
I have the following setup:
Play server running at localhost:9000
React app (created via create-react-app) running at localhost:3000
My JS code calls the /auth/linkedin endpoint which is implemented as follows:
Action { implicit req: RequestHeader =>
val csrfToken = CSRF.getToken.get.value
Redirect(linkedinUrl(oauthConfig.linkedinClientId, csrfToken)).withSession("state" -> csrfToken)
}
I have my Play application set to handle CORS appropriately.
My react app just makes a request to the above endpoint via Axios:
axios.get('/auth/linkedin')
This responds with a 303 with a redirect to the LinkedIn auth page which then gives me the error.
How do I get the CORS policy working correctly in this dev setup? I've tried adding the following to my package.json as the create-react-app documentation recommends:
"proxy": "http://localhost:9000",
And I've also tried setting a request header to "Access-Control-Allow-Origin" : "*" on the redirect in the Play server with no success.
Note that going to localhost:9000/auth/linkedin redirects properly.
https://www.linkedin.com/oauth/v2/authorization responses apparently don’t include the Access-Control-Allow-Origin response header, and because they do not, your browser blocks your frontend JavaScript code from accessing the responses.
There are no changes you can make to your own frontend JavaScript code nor backend config settings that’ll allow your frontend JavaScript code to make requests the way you’re trying directly to https://www.linkedin.com/oauth/v2/authorization and get responses back.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS explains in more detail but the gist of it is: for CORS, the server the request is being sent to must be configured to send the Access-Control-Allow-Origin response header, nor your own backend server.
2019-05-30 update
The current state of things seems to be that when needing to do LinkedIn authorization, you’ll have to initiate the request from your backend code. There’s no way you can do it from your frontend code, because LinkedIn no longer provides any support for it at all.
LinkedIn did previously provide some support for handling it from frontend code. But the page that documented it, https://developer.linkedin.com/docs/getting-started-js-sdk, now has this:
The JavaScript SDK is not currently supported
And https://engineering.linkedin.com/blog/2018/12/developer-program-updates has this:
Our JavaScript and Mobile Software Development Kits (SDKs) will stop working. Developers will need to migrate to using OAuth 2.0 directly from their apps.
So the remainder of this answer (from 2017-06-13) has now become obsolete. But it’s preserved below for the sake of keeping the history complete.
2017-06-13 details, now obsoleted
Anyway https://developer.linkedin.com/docs/getting-started-js-sdk has official docs that explain how to request authorization for a user cross-origin, which appears to be just this:
<script type="text/javascript" src="//platform.linkedin.com/in.js">
api_key: [API_KEY]
onLoad: [ONLOAD]
authorize: [AUTHORIZE]
lang: [LANG_LOCALE]
IN.User.authorize(callbackFunction, callbackScope);
</script>
And https://developer.linkedin.com/docs/signin-with-linkedin has docs for another auth flow:
<script type="in/Login"></script> <!-- Create the "Sign In with LinkedIn" button-->
<!-- Handle async authentication & retrieve basic member data -->
<script type="text/javascript">
// Setup an event listener to make an API call once auth is complete
function onLinkedInLoad() {
IN.Event.on(IN, "auth", getProfileData);
}
// Handle the successful return from the API call
function onSuccess(data) {
console.log(data);
}
// Handle an error response from the API call
function onError(error) {
console.log(error);
}
// Use the API call wrapper to request the member's basic profile data
function getProfileData() {
IN.API.Raw("/people/~").result(onSuccess).error(onError);
}
</script>
I ran into a similar problem, so let's divide this problem into detailed steps
Hit request to get the code(from frontend)
now send this code to the backend
In the backend, make another call to LinkedIn OAuth API and get the access token
With this access token make 3 separate calls to get the name, profile picture
and email of the user(yes you heard that right you need to make 3 separate calls and also the response JSON format is not very appealing)
Visit this for the detailed step-by-step process, it involves a lot of things. I can just share the process here but for the actual implementation visit this.
https://www.wellhow.online/2021/04/setting-up-linkedin-oauth-and-fixing.html
What could be done is:
window.location.href='http://localhost:9000/auth/linkedin'
The urlEndPoint could be directly to linkedIn's API or a back-end service which makes the call to linkedIn's API.

Embedded credentials alternatives for HTTP requests

A while ago I created a chrome extension called MalOnTheGo. It has been working well however chrome is now dropping support for the way I access resources from an API. The Chromestatus for the drop can be found here. They are dropping support for a format of urls called Embedded Credentials. I have looked for alternatives however I haven't been able to find anything.
In the API documentation they specify formatting the link in the same way I do using jQuery with the username and password parameters like this :
"Usage Examples:
CURL:
curl -u user:passwordhttps://myanimelist.net/api/account/verify_credentials.xml
This is one of the code snippets that chrome is alerting me will not work at some point in June.
function verifyCredentials(username, password, error, success) {
$.ajax({
"url": "https://myanimelist.net/api/account/verify_credentials.xml",
"error": error,
"username": encodeURIComponent(username),
"password": encodeURIComponent(password),
"success": success
});
}
The API's documentation states that this is the way to access that resource.
Is there anything I can change on my end or is this the only way I can use it and the API developers need to update their implementation?
Any alternatives to what I currently have would help
Thanks
You may find CORS to be helpful for making a cross-domain request to verify credentials. There is a lot of helpful info in this tutorial:
https://www.html5rocks.com/en/tutorials/cors/
You can still use ajax to make your request, you will just need to add some more headers for authentication. There is a section specifically for Chrome extensions as well:
https://www.html5rocks.com/en/tutorials/cors/#toc-cross-domain-from-chrome-extensions

Google OAuth2 - Exchange Access Code For Token - not working

I am currently in the process of implementing a server-side OAuth2 flow in order to authorize my application.
The JS application will be displaying YouTube Analytics data on behalf of a registered CMS account to an end user (who own's a channel partnered with the CMS account). As a result of this, the authorization stage needs to be completely hidden from the user. I am attempting to authorize once, then use the 'permanent' authorization code to retrieve access tokens as and when they're needed.
I am able to successfully authorize, and retrieve an access code. The problem begins when i attempt to exchange the access code for a token.
The HTTP POST Request to achieve this needs to look like this...
POST /o/oauth2/token HTTP/1.1
Host: accounts.google.com
Content-Type: application/x-www-form-urlencoded
code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=8819981768.apps.googleusercontent.com&
client_secret={client_secret}&
redirect_uri=https://oauth2-login-demo.appspot.com/code&
grant_type=authorization_code
I am using this code to achieve this:
var myPOSTRequest = new XMLHttpRequest();
myPOSTRequest.open('POST', 'https://accounts.google.com/o/oauth2/token', true);
myPOSTRequest.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
myPOSTRequest.send('code=' + myAuthCode + '&redirect_uri=http%3A%2F%2Flocalhost%2FCMSAuth3.html&client_id=626544306690-kn5m3vu0dcgb17au6m6pmr4giluf1cle.apps.googleusercontent.com&scope=&client_secret={my_client_secret}&grant_type=authorization_code');
I can successfully get a 200 OK response to this Request however no access token is returned, and myPOSTRequest.responseText returns an empty string.
I have played with Google's OAuth Playground - and can successfully get a token using my own credentials.
Am i missing something here?
You cannot do this, because there is the same origin policy. This is a security concept of modern browsers, which prevents javascript to get responses from another origin, than your site. This is an important concept, because it gives you the ability, to protect you against CSRF. So don't use the code authorization flow, use instead the token authorization flow.
Try and build up the full URL. Then dump it in a webbrowser. If its corect you will get the json back. You have the corect format.
https://accounts.google.com/o/oauth2/token?code=<myAuthCode>&redirect_uri=<FromGoogleAPIS>&client_id=<clientID>&client_secret={my_client_secret}&grant_type=authorization_code
Other things to check:
Make sure that you are using the same redirect_uri that is set up in google apis.
How are you getting the Authcode back? If you are riping it from the title of the page i have had issues with it not returning the full authcode in the title try checking the body of the page. This doesnt happen all the time. I just ocationally.

Avoid HTTP auth popup in a chrome extension (digest)

I'm currently developing a chrome extension, I need to access some http-auth protected resources (webdav). The HTTP auth is using (in the best case) a digest authentication.
I'm able to do the auth directly in the ajax request using the https://login:password#domain.tld/path/to/ressource form.
The issue is : if the login/password is wrong, I can't just get a 401 status (unauthorized), Chrome pops up the regular authentication dialog. Which I don't want cause it's confusing for user and I can't save the credentials from here.
EDIT: Another use-case I faced is : I want to check if a resource is password-protected without trying to provide credentials to actualy access it.
Any ideas on how to catch the 401 without poping the Chrome's auth box ?
Google Chrome teams has implented the onAuthRequired event in Google Chrome 22, so now is possible detect when the HTTP Basic Authentication is required.
In fact I wrote a extension that automatically sends the HTTP Basic Authentication credentials using the onAuthRequired event.
It is available for free in the official Google Chrome web store:
https://chrome.google.com/webstore/detail/basic-authentication-auto/dgpgkkfheijbcgjklcbnokoleebmeokn
Usage example of onAuthRequired event:
sendCredentials = function(status)
{
console.log(status);
return {username: "foo", password: "bar"};
}
chrome.webRequest.onAuthRequired.addListener(sendCredentials, {urls: ["<all_urls>"]}, ["blocking"]);
You need to add the right permissions to the manifest file in order to use the onAuthRequired.
"permissions": [ "http://*/*", "https://*/*", "webRequest", "webRequestBlocking", "tabs" ],
Download the extensions and check the source code for a better approach.
It should work even if the request was initiated from another extension.
It's really seems to be a lack in chrome behavior, other people are wishing to see something as the mozBakgroundRequest Chris highlighted, there already a a bug report for that.
There are (hackish) workarounds suggested by some developers in the bugtracker :
use a webworker and a timeout to perform the request
do the same with the background page
In both case, the benefit is that it won't pop an authentication box... But you will never know if it's a real server timeout or a 401. (I didn't tested those workarounds).
I think this is impossible. If you are using the browser's http client then it will prompt the user for credentials on a 401.
EDIT : Over in mozilla land https://developer.mozilla.org/en/XMLHttpRequest check out "mozBackgroundRequest".

Categories