How to handle OAuth access tokens - javascript

I have been exploring a couple of OAuth API's lately and bumped into an API that requires my page's code to handle and access token. I used the following in the HTML for example:
<script>
var client = thisapi.createClient({
accessToken: '0b7f6x59a0'
})
</script>
I am able to make calls to the API and retrieve the kind of data I need as long as I can access this token on my page. But it is exposed to the public. So how can I hide this kind of information?

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 use App Access Token to update Open Graph Tags

I want to dynamically update Open Graphs Tags on my website, but don't know how to do it properly.
I have understood you're not supposed to use the access token in a client side script, but how do I go about and update it otherwise.
I have seen examples online that does not use an access token, but I get an authorization error then.
Can I make any settings on facebook developer to skip access token or how can I make a call from my javascript, without showing the access token or app secret?
This is the part of my script which runs after modifying the meta:og tags:
//Update Open Graph
$.post(
'https://graph.facebook.com',
{
access_token: '123',
id: strCurrentUrl,
scrape: true
},
function(response){
console.log(response);
}
From Facebook:
for security, app access token should never be hard-coded into
client-side code, doing so would give everyone who loaded your webpage
or decompiled your app full access to your app secret, and therefore
the ability to modify your app. This implies that most of the time,
you will be using app access tokens only in server to server calls.

How to use a Refresh token to get access to google drive files

I am using the Drive API and the Google Sheets API to list users private and public spreadsheets using Google Console app.
I want the user to log in to our app the first time to view their files (this process is working). Now I want this user can access his files list without login after first-time authorization.
According to the Google guide, for this, I need to generate the access token and refresh token. I am having both the access token and refresh token.
here:
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer",
"refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
Now how I can use the refresh token and access token for users to avoid login in again and again. Please give a step-by-step process.
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer",
"refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
With the access_token you can get data from your google APIs. The token is specific to the API you have got it from. Use it as Authorization: Bearer 1/fFAGRNJru1FTz70BzhT3Zg).
You can use the refresh token to renew the access token. Access tokens expire after some time (3920 secs in your case).
The general Google API docs just say:
Access tokens have limited lifetimes. If your application needs access to a Google API beyond the lifetime of a single access token, it can obtain a refresh token. A refresh token allows your application to obtain new access tokens.
See https://developers.google.com/identity/protocols/oauth2?hl=en
Here's a step by step instruction available for the playground:
https://github.com/ivanvermeyen/laravel-google-drive-demo/blob/master/README/2-getting-your-refresh-token.md
Please have a look at this SO question/answer. An answer in this URL provides the following code:
window.gapi.client.init({
apiKey: this.GOOGLE.API_KEY,
clientId: this.GOOGLE.CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
}).then(()=>{
const authInstance = window.gapi.auth2.getAuthInstance();
authInstance.grantOfflineAccess()
.then((res) => {
console.log(res);
this.data.refreshToken = res.code;
});
});

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.

Token google analytics and relationship webPropertyId / profilId

I am currently developing a dashboard with Google Analytics API, which will be accessible website back office. I realized this during this developing javaScript I block on 2 things:
The first is the authentication must be transparent to the user via the use of a token.
In my approach I utlise OAuth2 of the API by generating a token with the playground for this token to be valid
I join my code
gapi.analytics.ready(function() {
var CLIENT_ID = 'XXXX.apps.googleusercontent.com';
var CLIENT_SECRET ='XXX...';
var ACCESS_TOKEN = 'XXX...';
var REFRESH_TOKEN ='XXXX....';
var EXPIRE_IN ='3600';
var TOKEN_TYPE ='Bearer';
var ACCESS_TYPE ='offline';
var SCOPE = 'https://www.googleapis.com/auth/analytics.readonly'
gapi.analytics.auth.authorize({
clientid: CLIENT_ID,
client_secret:CLIENT_SECRET,
serverAuth: {
access_token: ACCESS_TOKEN,
refresh_token: REFRESH_TOKEN,
//token_type: TOKEN_TYPE,
//expires_in: EXPIRE_IN,
//access_type: ACCESS_TYPE,
}
});
After the validity of the data are more accessible with a 401 error (logical because the token is no longer valid)
or to my first question about how to obtain a valid token all the time?
My second question concerns the recovery of data I based on the recovery of the profile number (like many such works).
However SEVERAL of my sites using the tracking number (UA-XXXXXXXX-N).
Knowing that sites use this number is the posibility to find the profilId thanks to the tracking number and accountId that lion can deduct.
But I do not know how to arive.
Es that someone already out how to make this relationship ???
Pending your answers thank you in advance
(Sorry for the translation I utlise google translation)
Authenticating using the playground is a bad idea, and wont work for long. You are going to have to code your own authentication process here. It sounds like you want to do this with your own websites this your own data, I would normally recommend you use a service account. A service account can be set up to authenticate without requiring the user to do anything. While some people say that you can use a Service account with JavaScript, I don't feel that it is a secure solution, I also wonder if it is ok to do this under the current terms of service. So my first recommendation to you is to look into using as service account with a server sided scripting language. say PHP. If you don't a user will have to authenticate and then they will only be seeing the information on there own website not your website.
Second how to find the Profile id:
The first and probably easiest option would be to just go to the admin section of Google analytics and find your profile id there. If you are looking for a way of doing this programmatically you, I would use the account summaries report from the Management API this will give you a list of all of the accounts for the current authenticated user you can then scan that to find the profile ids you want.

Categories

Resources