GreaseMonkey script to auto login using HTTP authentication - javascript

I've got quite a few GreaseMonkey scripts that I wrote at my work which automatically log me into the internal sites we have here. I've managed to write a script for nearly each one of these sites except for our time sheet application, which uses HTTP authentication.
Is there a way I can use GreaseMonkey to log me into this site automatically?
Edit: I am aware of the store password functionality in browsers, but my scripts go a step further by checking if I'm logged into the site when it loads (by traversing HTML) and then submitting a post to the login page. This removes the step of having to load up the site, entering the login page, entering my credentials, then hitting submit

It is possible to log in using HTTP authentication by setting the "Authorization" HTTP header, with the value of this header set to the string "basic username:password", but with the "username:password" portion of the string Base 64 encoded.
http://frontier.userland.com/stories/storyReader$2159
A bit of researching found that GreaseMonkey has a a function built into it where you can send GET / POST requests to the server called GM_xmlhttpRequest
http://diveintogreasemonkey.org/api/gm_xmlhttprequest.html
So putting it all together (and also getting this JavaScript code to convert strings into base64 I get the following
http://www.webtoolkit.info/javascript-base64.html
var loggedInText = document.getElementById('metanav').firstChild.firstChild.innerHTML;
if (loggedInText != "logged in as jklp") {
var username = 'jklp';
var password = 'jklpPass';
var base64string = Base64.encode(username + ":" + password);
GM_xmlhttpRequest({
method: 'GET',
url: 'http://foo.com/trac/login',
headers: {
'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.3',
'Accept': 'application/atom+xml,application/xml,text/xml',
'Authorization':'Basic ' + base64string,
}
});
}
So when I now visit the site, it traverses the DOM and if I'm not logged in, it automagically logs me in.

HTTP authentication information is sent on every request, not just to log in. The browser will cache the login information for the session after you log in the first time. So, you don't really save anything by trying to check if you are already logged in.
You could also forget about greasemonkey altogether and just give your login into on the url like so:
http://username:password#host/
Of course, saving this in a bookmark may be a security risk, but not more-so than saving your password in the browser.

Why don't you use Firefox (I assume you're using Firefox) to remember your credentials using the Password Manager?
I found this link: HTTP Authentication with HTML Forms. Looks like you can use javascript to do HTTP authentication. I don't think you can have Greasemonkey interrupt when you are first navigating to a URL though. You might have to setup some sort of launching point that you can use to have greasemonkey automatically redirect + login. For example, you can have the local page that takes the destination URL in the query string, have Greasemonkey automatically do the authenticate + redirect. The only problem is that you'll have to wrap the site bookmarks with your launching page for the bookmarks you use as entry points.

"http://username:password#host/" doesn't work on IE, FireFox works ok.

Related

How to share localstorage among different subdomains?

We have two separate websites / apps in same domain but different subdomains.
E.g.
https://hello.website.com (Webapp 1)
https://world.website.com (Webapp 2)
What we’d like to do is to login users at Webapp 1 and upon logging in and clicking a button within Webapp 1, we’d like to redirect the user to Webapp 2. However, Webapp 2 needs the same authentication token which is currently stored in the localstorage of Webapp 1. How do I make the localstorage content available to Webapp 2?
Or is there a better way to do this?
Since the domains are not the same, transferring information from the local storage of one of them to another isn't possible directly, but since the sites are on HTTPS, it should be safe and easy enough to send the authentication token as search parameters. For example, when redirecting, instead of redirecting to https://world.website.com, instead take the current authentication token for https://hello.website.com and append it, then redirect:
const url = 'https://world.website.com?token=' + authToken;
window.location.href = url;
(if the authentication token may have special characters in it, you may need to escape them first)
Then, on the other domain, check to see if there's a token in the search parameters, and if so, extract it and save it to localStorage:
const paramsMatch = window.location.href.match(/\?.+/);
if (paramsMatch) {
const params = new URLSearchParams(paramsMatch[0]);
const authToken = params.get('token');
if (authToken) {
localStorage.authToken = authToken;
}
}
Because the domains are on HTTPS, putting the token in the URL is mostly safe - eavesdroppers will not be able to see it. But if your server that handles the requests saves logs, you may find it undesirable for the server to have its logs include authentication tokens as a result of this approach.
Another way would be for:
Webapp 1 to make a POST request to Webapp 2 with the token in the payload (where Webapp 2 has the appropriate CORS settings)
Webapp 2 generates a new unique URL (that expires after, say, 30 minutes), associates the token with that URL, and responds to the client on Webapp 1 with the URL
The client receives the response from Webapp 2 and then navigates to the unique URL on Webapp 2 that it was just given
Webapp 2, when handling the request, sees that the unique URL was associated with a token, and goes through the process of fully associating that token with the request session
That's the limitation of localstorage and sessionstorage. You can't. There are some workarounds with iframe, but those are neither elegant nor secured. You should use cookie with appropriate domain attribute domain=example.com. You may also want to read the following answer for security with cookie vs localstorage: https://stackoverflow.com/a/54258744/1235935

I need to send and listen to events from a 3rd party JavaScript API

We are a social start-up and are given a great opportunity by one of the biggest banks in our country. Basically they will feature our website in their Mobile App, and users can access our website via their app in an iFrame. We are currently working on integrating a SSO log in flow between their app and our website, so users are logged in immediately when they open our website in their app.
For this, they have created an API that we're able to use. The API requires 2 main things to get the flow working:
send a pageLoaded() event when the DOM is ready, when this event is sent, we get returned a token to fetch the user's personal information with a public event.
send a pageReady() event when the backend logic processing is done (eg account created and user logged in).
They will show a spinner in their app, until the pageReady event is being sent to them.
My website uses PHP and JS (jQuery) as the main technologies. I am having a number of issues on how to implement this correctly.
This is the code I am using as of now, it works in a certain way, but it's very troublesome for the reasons mentioned below the code snippet:
$(document).ready(function(){
var getUrl = window.location;
var baseUrl = getUrl.protocol + "//" + getUrl.host;
/*** Mobile App JS Code ***/
var receiver = new PostmessageExternalReceiver();
var sender = new PostmessageExternalSender();
receiver.addTokenEventListener(function (api, token, version){
if (api == 'init') {
$.ajax({
type: 'post',
cache: false,
url: baseUrl + '/login/abc/abcLogin',
data: {
'token': token
},
dataType: 'json',
success: function(response){
sender.pageReady();
window.location.replace(response.redirect_url);
}
});
}
});
sender.pageLoaded();
});
These are the problems that i'm not sure how to get around:
Since the sender.pageLoaded(); is in the jQuery document ready, and after performing the AJAX request, we are redirecting again to the same page (the homepage), because after the redirect, the user will be logged in and some extra blocks will show on the homepage. So if we are on the homepage again, the document ready will fire yet another sender.pageLoaded() event, and we are stuck in an infinite redirecting loop.
Right now I am including all 4 API javascript libraries provided by our third party, and my own javascript file including the AJAX request and the pageLoaded() and pageReady(). This is only applicable for users that come to our website via the 3rd party mobile app. For all other visitors in our website this is not applicable and requires extra resources to be loaded in when they're not used. (the ajax request will never be executed for them because a token will not be sent via the mobile app, but still we do not want to send a pageLoaded() for every visitor on our website, even if he is not from the mobile app.
This is a nice-to-have question: since we need to send the pageReady() after our PHP logic (via AJAX request) is done, we cannot do the redirect in PHP, so I return the redirect_url in my AJAX and then do the window.location.replace() method with the redirect URL. In the 3rd party app, this will cause the app to remove the spinner (on the pageReady() event) and show our website, but immediately afterwards we will redirect the user using the window.location.replace() , this causes a refresh of the page, and thus a small annoyance and not so smooth experience for the user. They expect after the spinner is gone that the website is immediately accessible. How would I go around this?
Thanks a lot in advance!

Access facebook canvas URL parameters from my app

I'm making a facebook canvas app (a game) and I want my players to use links with url parameters that link to different areas in the game, i.e.
https://apps.facebook.com/mgatlandtestapp?level=emeraldhillzone
How do I access the level=emeraldhillzone information in my app (either client-side with javascript in an iFrame, or server-side as a node.js application)
Things I've tried so far:
This 4-year-old answer says it's possible to in the client-side code but doesn't say how (sorry if I'm missing something obvious! window.location gives the location of my heroku server, not the facebook url.)
Some of the facebook documentation made me think this would work:
Use a url in the form https://apps.facebook.com/mgatlandtestapp?app_data=yourDataHere then on the server side get the signed request sent to the server and it will contain an app_data field.
However when I did this, the signed request did not contain an app_data field.
After reading more, I think the app_data thing only works if the app is being loaded within a facebook Page Tab, not a facebook Canvas Page.
Facebook sends an initial POST to your app, which includes the url parameters. You can access the url parameters from in that POST request.
I used this url to load the page:
https://apps.facebook.com/mgatlandtest/?level=emeraldhillzone
I used this code in my node.js server app to serve the request
app.post('/*', function(request, response) {
console.log('url parameters:');
console.log(request.query);
response.redirect('/');
});
The output in the node console was:
url parameters:
{ level: 'emeraldhillzone' }
You could then use the parameters to redirect to a different page.
This was actually really simple, once I got past all the distracting misinformation. The key was searching for 'deep linking' (instead of 'url parameters'), which brought up this answer

Simple, library-free, Javascript method for inserting a file into Google Drive

I am using a web scripting service that generates files and can POST them to a URI. If I don't have or provide one, they are stored on an ftp server. What I want to do is upload these files to my Google Drive, and, eventually, other users' Drives as well.
Because I'm using a 'private' host, and 'pure' javascript--no browser-- I can't add any framework or library. I have to do everything by hand.
I've been through all the documentation, read many posts here, found some really good information, and have lost some hair.
Here's where the project is at the moment:
I'm using oauth2, web server flow.
I can request access, and get an auth code, like so:
https://accounts.google.com/o/oauth2/auth?
response_type=code
&scope=https://www.googleapis.com/auth/drive
&redirect_uri=http://myserver.com/
&access_type=offline
&state=testing
&client_id={CLIENT_ID].apps.googleusercontent.com
This gets me an auth code. So far, so good.
Then, I take that code and do:
POST /o/oauth/token HTTP/1.1
Host: https://accounts.google.com
client_id=yada_yada
&client_secret=more_yada
&redirect_uri=http://myserver.com/
&code=[the code I just got]
&grant_type=authorization_code
Now, this step fails if I use XMLHttpRequest; I get an "Access-Control-Allow-Origin' violation. So, I slapped together an HTML page with a form and posted that. I get back a JSON object containing an access_token and a refresh_token. Yay.
{
'access_token' : [Access Yada-Yada],
'expires_in' : 3600,
'token_type' : 'Bearer',
'refresh_token' : '[Refresh Yada-Yada]
}
Now, I can get my script to work for one hour. Yippee.
Now, after all that, how do I use the refresh_token to get a new access_token. I have tried everything. XMLHttpRequests give me the same cross-domain violation, and doing an HTML form gets me an 'invalid_request' response! I tried using curl, never having touched it before, and that did not go well.
Please help.
Edit
The format for the refresh request goes like this:
var xhr = XMLHttpRequest();
xhr.open('POST',"https://accounts.google.com/o/oauth/token?" +
"client_id=[CLIENT_ID]"+
"&client_secret=[CLIENT_SECRET]"+
"&refresh_token=[REFRESH_TOKEN]"+
"&grant_type=refresh_token",
true);
xhr.setRequestHeader('Content-Type','x-www-form-urlencoded');
xhr.send();
I copied the url parameters directly from the OAuth Playground, which I had given my project keys. I get the Access-Control-Allow-Origin violation when I do this.
If I wrap it in a form, like so:
<form method="GET" action="https://accounts.google.com/o/oauth2/token">
<div>REFRESH TOKEN:</div>
<input type="text" name="refresh_token" value="[REFRESH_TOKEN]">
<br><div>CLIENT ID:</div>
<input type="text" name="client_id" value="[CLIENT_ID]">
<br><div>CLIENT SECRET:</div>
<input type="text" name="client_secret" value="[CLIENT_SECRET">
<br><div>GRANT TYPE:</div>
<input type="text" name="grant_type" value="refresh_token">
</form>
I can get a new access token. But, I can't get the same thing to work in JS using XMLHttpRequest. I keep getting XD errors.
I just need a way to post a refresh request to google from javascript without using any libraries.
The webserver flow is not adequate for an application that only has client-side logic. I recommend you use the G+ Sign-In widget (which can be customized for any scope, including Drive and actually works for all Google users whether or not they happen to have a Google+ account, including all Google Apps users). https://developers.google.com/+/
If you'd rather do it all yourself, you can read on how the client-side flow works: Essentially you ask for 'response_type=token' and parse the access_token from the fragment. There's no refresh_token for access_token exchange step. You can periodically refresh the token in the background using an invisible iframe by just repeating the initial authorization request. make sure you add display=none to requests you make in invisible iframe. See https://developers.google.com/accounts/docs/OAuth2UserAgent for more information.

Google OAuth WildCard Domains

I am using the google auth but keep getting an origin mismatch. The project I am working has sub domains that are generated by the user. So for example there can be:
john.example.com
henry.example.com
larry.example.com
In my app settings I have one of my origins being http://*.example.com but I get an origin mismatch. Is there a way to solve this? Btw my code looks like this:
gapi.auth.authorize({
client_id : 'xxxxx.apps.googleusercontent.com',
scope : ['https://www.googleapis.com/auth/plus.me',
state: 'http://henry.example.com',
'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile'],
immediate : false
}, function(result) {
if (result != null) {
gapi.client.load('oath2', 'v2', function() {
console.log(gapi.client);
gapi.client.oauth2.userinfo.get().execute(function(resp) {
console.log(resp);
});
});
}
});
Hooray for useful yet unnecessary workarounds (thanks for complicating yourself into a corner Google)....
I was using Google Drive using the javascript api to open up the file picker, retrieve the file info/url and then download it using curl to my server. Once I finally realized that all my wildcard domains would have to be registered, I about had a stroke.
What I do now is the following (this is my use case, cater it to yours as you need to)
On the page that you are on, create an onclick event to open up a new window in a specific domain (https://googledrive.example.com/oauth/index.php?unique_token={some unique token}).
On the new popup I did all my google drive authentication, had a button to click which opened the file picker, then retrieved at least the metadata that I needed from the file. Then I stored the token (primary key), access_token, downloadurl and filename in my database (MySQL).
Back on step one's page, I created a setTimeout() loop that would run an ajax call every second with that same unique_token to check when it had been entered in the database. Once it finds it, I kill the loop and then retrieve the contents and do with them as I will (in this case I uploaded them through a separate upload script that uses curl to fetch the file).
This is obviously not the best method for handling this, but it's better than entering each and every subdomain into googles cloud console. I bet you can probably do this with googles server side oauth libraries they use, but my use case was a little complicated and I was cranky cause I was frustrated at the past 4 days I've spent on a silly little integration with google.
Wildcard origins are not supported, same for redirect URIs.
The fact that you can register a wildcard origin is a bug.
You can use the state parameter, but be very careful with that, make sure you don't create an open redirector (an endpoint that can redirect to any arbitrary URL).

Categories

Resources