using xmlHTTPRequest for opening a secure application in a new window - javascript

We have a requirement where we want the ability to launch a new application (NewApp) from the base app (BaseApp). The login credentials for BaseApp and the NewApp are same since they use the same identity store.
BaseApp and NewApp are on different tech stacks, both being web applications. they are hosted on different servers.
NewApp provides basic Auth for web access.
How can I use java script to launch NewApp from links on my BaseApp. I want to be able to inject Basic Auth credentials.
I have tried using xmlHTTPRequest, but it appears to be geared more towards calling REST services and processing the fetched data rather than launching URLs.
using java script window.open(url) does not let me inject authorization header!
anything else I can explore ?
Additional Info:
The session cookie for BaseApp is not applicable for NewApp. When we first complete Basic Auth on the BaseApp, it generates a new Session Cookie which needs to be used.
Through xhr we are able to call the URL (with Basic Auth) for the NewApp. This returns a response with a session cookie.
The problem that I am unable to figure out is, how to render the response on a new window/tab on the browser.

The simplest solution is calling the new window with following url
http://<user>:<pass>#newapp
but this is not a good solution because you see credentials in plain text. You should better try one of the following solutions:
If you use a session cookie it's often possible to append it to the url like http://newapp?JSESSIONID=kjasbfkji877786sdf.
Either you can use the same Cookie from your base app (that should not be possible if the applications are on different machines)
Create XMLHttpRequest calling NewApp and authentication via BasicAuth. The response header should contain the required session cookie from the other server. Then you can call NewApp with the required cookie.
If you have no session cookie you could also try the second solution (XHR) because if a session is created it is also stored in the browser and the browser can manage session handling itself.
So in cases 1.2. and 2. you should do the following:
var XMLReq = new XMLHttpRequest();
XMLReq.open("GET", "url", asynchronous?, "user", "password");
XMLReq.send(null);
In case 1.2. you need to provide an additional handler for a succeeding call:
XMLReq.onload = function() {
window.open('http://NewApp?JSESSIONID='+XMLReq.getResponseHeader('Cookie'));
// note that 'JSESSIONID' depends on your backend implementation, this example is made for java backend
}
In case 2. it's simply
XMLReq.onload = function() {
window.open('http://NewApp');
}
The best solution would be OAuth or OpenID in this case but that depends on your usecase and your system environment and requires maybe a more complex backend implementation if there is currently no support for OAuth or OpenID.

Related

Single flow: sign user in via Google oAuth AND grant offline/server access?

I'm trying to implement Google sign-in and API access for a web app with a Node.js back end. Google's docs provide two options using a combo of platform.js client-side and google-auth-library server-side:
Google Sign-In with back-end auth, via which users can log into my app using their Google account. (auth2.signIn() on the client and verifyIdToken() on the server.)
Google Sign-in for server-side apps, via which I can authorize the server to connect to Google directly on behalf of my users. (auth2.grantOfflineAccess() on the client, which returns a code I can pass to getToken() on the server.)
I need both: I want to authenticate users via Google sign-in; and, I want to set up server auth so it can also work on behalf of the user.
I can't figure out how to do this with a single authentication flow. The closest I can get is to do the two in sequence: authenticate the user first with signIn(), and then (as needed), do a second pass via grantOfflineAccess(). This is problematic:
The user now has to go through two authentications back to back, which is awkward and makes it look like there's something broken with my app.
In order to avoid running afoul of popup blockers, I can't give them those two flows on top of each other; I have to do the first authentication, then supply a button to start the second authentication. This is super-awkward because now I have to explain why the first one wasn't enough.
Ideally there's some variant of signIn() that adds the offline access into the initial authentication flow and returns the code along with the usual tokens, but I'm not seeing anything. Help?
(Edit: Some advice I received elsewhere is to implement only flow #2, then use a secure cookie store some sort of user identifier that I check against the user account with each request. I can see that this would work functionally, but it basically means I'm rolling my own login system, which would seem to increase the chance I introduce bugs in a critical system.)
To add an API to an existing Google Sign-In integration the best option is to implement incremental authorization. For this, you need to use both google-auth-library and googleapis, so that users can have this workflow:
Authenticate with Google Sign-In.
Authorize your application to use their information to integrate it with a Google API. For instance, Google Calendar. 
For this, your client-side JavaScript for authentication might require some changes to request
offline access:
$('#signinButton').click(function() {
auth2.grantOfflineAccess().then(signInCallback);
});
In the response, you will have a JSON object with an authorization code:
{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}
After this, you can use the one-time code to exchange it for an access token and refresh token.
Here are some workflow details:
The code is your one-time code that your server can exchange for its own access token and refresh token. You can only obtain a refresh token after the user has been presented an authorization dialog requesting offline access. If you've specified the select-account prompt in the OfflineAccessOptions [...], you must store the refresh token that you retrieve for later use because subsequent exchanges will return null for the refresh token
Therefore, you should use google-auth-library to complete this workflow in the back-end. For this,
you'll use the authentication code to get a refresh token. However, as this is an offline workflow,
you also need to verify the integrity of the provided code as the documentation explains:
If you use Google Sign-In with an app or site that communicates with a backend server, you might need to identify the currently signed-in user on the server. To do so securely, after a user successfully signs in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity of the ID token and use the user information contained in the token
The final function to get the refresh token that you should persist in your database might look like
this:
const { OAuth2Client } = require('google-auth-library');
/**
* Create a new OAuth2Client, and go through the OAuth2 content
* workflow. Return the refresh token.
*/
function getRefreshToken(code, scope) {
return new Promise((resolve, reject) => {
// Create an oAuth client to authorize the API call. Secrets should be
// downloaded from the Google Developers Console.
const oAuth2Client = new OAuth2Client(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
// Generate the url that will be used for the consent dialog.
await oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope,
});
// Verify the integrity of the idToken through the authentication
// code and use the user information contained in the token
const { tokens } = await client.getToken(code);
const ticket = await client.verifyIdToken({
idToken: tokens.id_token!,
audience: keys.web.client_secret,
});
idInfo = ticket.getPayload();
return tokens.refresh_token;
})
}
At this point, we've refactored the authentication workflow to support Google APIs. However, you haven't asked the user to authorize it yet. Since you also need to grant offline access, you should request additional permissions through your client-side application. Keep in mind that you already need an active session.
const googleOauth = gapi.auth2.getAuthInstance();
const newScope = "https://www.googleapis.com/auth/calendar"
googleOauth = auth2.currentUser.get();
googleOauth.grantOfflineAccess({ scope: newScope }).then(
function(success){
console.log(JSON.stringify({ message: "success", value: success }));
},
function(fail){
alert(JSON.stringify({message: "fail", value: fail}));
});
You're done with the front-end changes and you're only missing one step. To create a Google API's client in the back-end with the googleapis library, you need to use the refresh token from the previous step.
For a complete workflow with a Node.js back-end, you might find my gist helpful.
While authentication (sign in), you need to add "offline" access type (by default online) , so you will get a refresh token which you can use to get access token later without further user consent/authentication. You don't need to grant offline later, but only during signing in by adding the offline access_type. I don't know about platform.js but used "passport" npm module . I have also used "googleapis" npm module/library, this is official by Google.
https://developers.google.com/identity/protocols/oauth2/web-server
https://github.com/googleapis/google-api-nodejs-client
Check this:
https://github.com/googleapis/google-api-nodejs-client#generating-an-authentication-url
EDIT: You have a server side & you need to work on behalf of the user. You also want to use Google for signing in. You just need #2 Google Sign-in for server-side apps , why are you considering both #1 & #2 options.
I can think of #2 as the proper way based on your requirements. If you just want to signin, use basic scope such as email & profile (openid connect) to identify the user. And if you want user delegated permission (such as you want to automatically create an event in users calendar), just add the offline access_type during sign in. You can use only signing in for registered users & offline_access for new users.
Above is a single authentication flow.

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

Using IdentityServer3 with a standalone EXE (without using 'redirect_uri'?)

I have a web site I'm developing that uses IdentityServer3 to authentication with, and it also returns an access token to enable access to my APIs. All works well. I'm now rewriting the web site into a standalone windows EXE. The EXE is written in React and Electron to make it a standalone application. My problem is: I call the 'authorize' endpoint to my identityserver3 server, but I do not know what to put in for the 'redirect_uri' required by identityserver3? Since my EXE is standalone it has no uri address?
Is there a way to use IdentityServer3 as an API, where I can send the 'authorize' url to IdentityServer3, and have it return the access token as a response (to the API call?)
You can do as Kirk Larkin says - use the Client Credentials flow.
The following code is in .NET:
var client = new TokenClient(
BaseAddress + "/connect/token",
"clientId",
"clientSecret");
var result = client.RequestClientCredentialsAsync(scope: "my.api").Result;
var accessToken = result.AccessToken;
Where BaseAddress is your IDS address.
Of course you will have to register your client in the IDS clients list with the appropriate flow (Client Credentials), and the scope is just optional, but I guess you will need one.
Then accessing a protected API is fairly easy:
var client = new HttpClient();
client.SetBearerToken(accessToken);
var result = client.GetStringAsync("https://protectedapiaddress").Result;
EDIT: For JavaScript approach:
This and this look like a working solution, but I've tried neither of them

How to make sure that only a specific domain can query from your REST api?

I have an app that has a REST api. I want it so that the only requests that can be made to the REST api are ones originating from the app itself. How can I do that?
I am using a node.js+express server too.
EDIT: the app is fully a public web app.
Simply define the header in your request, what this does is, it allows requests only from a certain domain, and instantly rejects any other domain.
response.set('Access-Control-Allow-Origin', 'domain.tld');
EDIT: IF you're really keen against web scraping stuff, you could make a function to double check client's origin.
function checkOrigin (origin) {
if (origin === "your.domain.tld") {
return true;
} else {
return false;
}
}
/* Handling it in response */
if (checkOrigin(response.headers.origin)) {
// Let client get the thing from API
} else {
response.write("Send them error that they're not allowed to use the API");
response.end();
}
Above example should work for the default HTTP/HTTPS module, and should also work for Express, if I'm not mistaken.
EDIT 2: To back my claim up that it should also work for Express, I found this quotation at their documentation;
The req (request) and res (response) are the exact same objects that Node provides, so you can invoke req.pipe(), req.on('data', callback), and anything else you would do without Express involved.
I would recommend using an API key from the client. CORS filters are too easy to circumvent.
A simple approach for securing a How to implement a secure REST API with node.js
Overview from above post:
Because users can CREATE resources (aka POST/PUT actions) you need to secure your api. You can use oauth or you can build your own solution but keep in mind that all the solutions can be broken if the password it's really easy to discover. The basic idea is to authenticate users using the username, password and a token, aka the apitoken. This apitoken can be generated using node-uuid and the password can be hashed using pbkdf2
Then, you need to save the session somewhere. If you save it in memory in a plain object, if you kill the server and reboot it again the session will be destroyed. Also, this is not scalable. If you use haproxy to load balance between machines or if you simply use workers, this session state will be stored in a single process so if the same user is redirected to another process/machine it will need to authenticate again. Therefore you need to store the session in a common place. This is typically done using redis.
When the user is authenticated (username+password+apitoken) generate another token for the session, aka accesstoken. Again, with node-uuid. Send to the user the accesstoken and the userid. The userid (key) and the accesstoken (value) are stored in redis with and expire time, e.g. 1h.
Now, every time the user does any operation using the rest api it will need to send the userid and the accesstoken.

Testing parser for oauth2 implicit grant access_token in javascript

I'm writing angular application that uses implicit grant oauth strategy. If I don't have valid access token in my cookies I am redirected to web interface of authentication server, input my credentials and get redirected to my site with access token in the url. My system parses it and writes down into cookies.
Currently I faced question of unit testing this parse function that consumes the url and returns access token object. Can't think the good way, so writing here:
1. How do you approach unit testing (so I can't make direct request to working oauth server) a function that parses the access token from authentication server?
2. How do you build url params with access token? Will it be secure if I copy current access token and use it in test data?
3. Are there libraries that can aid creation of mock access token object?
You could breakout "just enough" OAuth, like the service linked below. This will give you a super-basic OAuth provider, but is really geared to "integration testing" scenarios (depending on where you draw the lines on these things).
If you want to be a unit-testing purist/zealot, then you could fork this into your app's unit test code.
https://github.com/zalando-stups/mocks/tree/master/oauth2-provider
In lieu of a great answer, here's one to get you out of a hole :)
After approaching authors of my oAuth authentification service I've got insight on the data the server was sending me in case of successful authentication. In my case the response from oAuth server has the following form:
header // encoded algorithm for has creation
access token // base64 encoded object
hash // of the 3 previous items done using algorithm specified in the header.
It has the following structure: header.access_token.hash
My fear for security breach was concerned about putting correct data (that I can get from browser) into the test files. But as it turns out the token will not be valid without correct hash. The hashing of the token data is done in server side using private key, so if I change the hash the token will become obsolete.
As my token-parsing-function that I wanted to check uses only the access_token part of the request, I've changed the header and hash parts and also encoded other user name for sake of testing purposes.
So this is the resolution of my current problem. Hope this will help somebody.

Categories

Resources