Google Client Refresh Token only works once - javascript

I am using the Google API Client to access the Directory API. My code is as follows:
function getClient()
{
$client = new Google_Client();
$client->setApplicationName('G Suite Directory API');
$client->setScopes(Google_Service_Directory::ADMIN_DIRECTORY_USER);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
.....
//this is where google creates the initial token
}
}
}
My problem revolves around this line:
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
When I initially authorise the client, I get a token.json that contains a refresh token. After an hour, the token expires and it creates a new token. This new token however does not include a refresh token. So it will only ever refresh once and then stop working after 2 hours.
Is there a setting I'm missing?
I had to add $client->setApprovalPrompt('force'); in order for the initial token to include a refresh token.

You should keep reusing the refresh token you got initially to get new access token every hour (which you'd then use in each API request). Refresh tokens generally don't expire.

Related

How to configure msalv2 js library to renew token on ExpiresOn instead of extExpiresOn?

I'm trying to use msalv2 js library to do SSO authentication. It mostly works but I find an issue with token expiring and how to renew it.
I am calling the aquireTokenRedirect function to get the new token and auto renew it but I see that it keeps returning the same token. I try to auto renew it when my server side code detects that the token is expired. It uses a msal jwt library to check.
My guess as to what is happening is, my app is sending the id token to the server (I use that instead of access token), and the server decodes it and sees the exp value, which is the ExpiresOn value from the msal js response.
When this time has expired, it will return a 401 error which is fine. However then I try to renew it on the client side, but find it gives me back the same token value...
I check the msal response from earlier and notice there is a extExpiresOn value which is a little later in time. So now i'm thinking the msal library only renews on this time.
So the question is, how can I configure msalv2 js library to renew on the ExpiresOn instead of the extExpiresOn?
Thanks
function getTokenRedirect(request) {
/**
* See here for more info on account retrieval:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
*/
request.account = myMSALObj.getAccountByUsername(username);
return myMSALObj.acquireTokenSilent(request)
.catch(error => {
console.warn("silent token acquisition fails. acquiring token using redirect");
if (error instanceof msal.InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenRedirect(request);
} else {
console.warn(error);
}
});
}

Returning back refresh token for google Oauth 2 using php client

I'm using the JS library https://apis.google.com/js/platform.js to log in my user to pass a token to my backend:
gapi.auth2.getAuthInstance().signIn().then(() => {
var user = gapi.auth2.getAuthInstance().currentUser.get();
var authResp = user.getAuthResponse();
var bodyFormData = new FormData();
bodyFormData.append('google_token', authResp.access_token);
When I receive the access token on my server I try use the php client library to generate the rest of the client:
$google = new google();
$google->setAuthConfig('client_secrets.json');
$google->setAccessType('offline');
$google->createAuthUrl();
$google->getRefreshToken();
$google->setAccessToken($thePOSTedAccessToken);
$service = new Google_Service_MyBusiness($google);
The issue is no matter what I do I cannot get my refresh token from client.auth after I create the $client->createAuthUrl(); . So this either means I'm not doing the client properly in php or something else is wrong. I'm asking this question because I've followed all documentation and looked extensively why I'm not receiving my refresh.
You can't mix things like this. If the authorization code was created using JavaScript you will not be able to get a refresh token as JavaScript does not support refresh tokens.
If you want a refresh token then you will need to authorize the user using a server-side language and not a client-side language.
When you you run the code the first time, the user will be displayed with a consent screen. If they consent to your access then you get a code returned. This is an authorization code.
In the example below you can see how the authorization code is exchanged for an access token and refresh token.
Only after $client->authenticate($_GET['code']); is called will $client->getAccessToken(); and $client->getRefreshToken(); work.
Also once the code has been run once, if you try to authorize the user again you will not get a new refresh token, Google assumes that you saved the refresh token they already sent you. If you didn't save it, you would then need to revoke the user's access either using the revoke command in the library or by going to the user's Google account and revoking the application that way.
Sorry, all I have on hand is a web version; not sure if you're running this command-line or web-based.
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/Oauth2Authentication.php';
// Start a session to persist credentials.
session_start();
// Handle authorization flow from the server.
if (! isset($_GET['code'])) {
$client = buildClient();
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$client = buildClient();
$client->authenticate($_GET['code']); // Exchange the authencation code for a refresh token and access token.
// Add access token and refresh token to session.
$_SESSION['access_token'] = $client->getAccessToken();
$_SESSION['refresh_token'] = $client->getRefreshToken();
//Redirect back to main script
$redirect_uri = str_replace("oauth2callback.php",$_SESSION['mainScript'],$client->getRedirectUri());
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
?>

How can access google calendar of user and edit it without asking for user permisssion again and again

On my website, I am asking for google calendar access. I can edit the user calendar but, I don't want to ask for user permission, again and again, so once the user authorized and give access to google calendar, I can edit it anytime until the user revokes the access. Should I implement it on the frontend or the backend and how? I checked few answers where they mention we can use a service account but, it is not clear how can I edit or read the individual user's calendar events and how can I remove it once the user revokes access. This question was deleted because code was missing so adding code below.
I tried this so once user login I get access token and I am using it
window.gapi.load("client:auth2", () => {
window.gapi.client.setApiKey("api_key");
window.gapi.client.load("https://content.googleapis.com/discovery/v1/apis/calendar/v3/rest")
.then(() => {
window.gapi.auth.setToken({ access_token: access_token })
window.gapi.client.calendar.events.insert({
"calendarId": "id",
'resource': event
}).then((res) => {
console.log("calendar data res "+JSON.stringify(res))
}).catch(err => console.log("error getting calendar data "+JSON.stringify(err)))
}).catch(err => console.error("Error loading GAPI client for API", err) )
})
but once access token expires how can I get a new access token( I don't want to show login popup to the user again and again. I want to know how can I do it using refresh token on client-side).
You can't get a refresh token on the client-side without exposing your secret key to the public.
You can create an endpoint that accepts oAuth code and return the token, save the refresh token for later. You set up a corn job that checks for expired token and refreshes them.
Every time the user accesses your app, you grab a fresh token from the server and proceed to work normally.
As per Google guidelines. You do POST to https://oauth2.googleapis.com/token. Assuming your server-side stack is in Node.js, you do something like this using an HTTP client like Axios:
const Axios = require('axios');
const Qs = require('querystring');
const GOOGLE_CLIENT_ID = 'abc';
const GOOGLE_CLIENT_SECRET = '123';
let refreshToken = getFromDataBase(); // should be stored in database
Axios.post('https://oauth2.googleapis.com/token', Qs.stringify({
client_id: GOOGLE_CLIENT_ID,
client_secret: GOOGLE_CLIENT_SECRET,
refresh_token: refreshToken,
grant_type: 'refresh_token'
}), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
})
.then(({ data }) => console.log(data.access_token)) // new token that expires in ~1 hour
.catch(console.log)
Firstly, do you (a) want to update the calendar when the user is not logged in, for example in response to an external event? ... OR ... do you (b) only want to update the calendar from within a browser session?
If (a), then you need to ask the user for offline access which will give you a Refresh Token , which you can securely store on a server and use whenever you need to. (Forget all about Service Accounts).
If (b), then you need the following pieces of information :-
When the access token expires, request access again, but add the flag prompt=none. This will give you a fresh Access Token without the user seeing any UX.
Do this in a hidden iframe so that it is happening in the background and is invisible to the user. Your iframe will therefore always have an up to date Access Token which it can share with your app via localStorage or postMessage.

Keep refreshing JsonWebToken by any action form user

I use JsonWebtoken to create an access token for authentication purposes in my web app in node js using express.
I want to define an expiry date for this token but I don't know how It refreshes the "iat" by performing some activities by the user! basically, I want the expiry date starts over again if the user performs some activity within the period of 30 minutes since the last activity!
jwt.sign({ _userName: userName, _name: name + ' ' + sureName, _role: role.name }, config.get('jwtPrivateKey'),);
This is how I create the token.
So the question is how can I refresh the token and send a new one in case of activity from the user within 30 minutes so that we can make sure that the user does not need to login in 30 minutes and the token is going to be valid ?! and then I want the token expires if the user does not perform any tasks for more than 30 minutes!
The standard way to refresh an access token is to create a separate token, a "refresh token" (literally). Here is a blog post to get you started, blog post.
The basic idea is to send both tokens to the client. The access token expires in X time, and the refresh token expires in a much longer amount of time. Once the client gets an error from the server (unauthenticated), it sends another request to the server asking for a new access token. It passes the refresh token when making this request. The server checks if the refresh token is valid, and if so it will return a new refresh/access token pair to the client. It's important that the refresh token can only be used to get new access tokens, and the access token is used for retrieving data from the server.
I fix it using this, so that I can generate a new one in case I need it
app.use(function (message, req, res, next) {
try {
if (typeof message === 'string') {
let userName = req.body._userName;
let name = req.body._name;
let role = req.body._role;
let token = generateToken(userName, name, role);
res.header('z-auth-token', token).status(200).send(message);
} else {
next(message);
}
} catch (e) {
next(e);
}
});

Best way to refresh JWT token using Node.js

I have created node.js backend. On Login i am sending a jwt token. For user experience i don't want them to re-login but instead get their tokens refreshed, which i have set to expire in 4 hours.
However i am not getting a good lead on how to do this effectively. My idea is to provide a button in client side, by clicking on which user can get their tokens refreshed. Assuming a rest call that i can make from client side, i need help in its implementation. Appreciate it.
if (response) {
bcrypt.compare(req.body.password, response.password, (error, result) => {
if (result) {
const token = jwt.sign(
{
email: response.email,
userId: response._id
},
process.env.JWT_KEY,
{
expiresIn: '4h'
});
return res.status(200).json({
message: 'Auth Successful! User Found. ',
token
})
} else {
return res.status(404).json({
message: 'Auth Failed! User Not found'
})
}
}
You would need two tokens:
Refresh Token (will be saved in db)
Access Token (your JWT which will expire quickly e.g. 10 mins)
Refresh token typically does not expire quickly. However, there may be a challenge on how to secure the refresh token.
you also need to change the refresh token in the database every time the user refreshed their token / logs in.
You also need to store expiry_date of your access token (you can make it a response from your login api).
Then, in your front-end, you can store those tokens in localStorage / sessionStorage depending on your security requirements.
Then, each API call would check the expiry date that you've set. And if it reaches a certain threshold (e.g. 5 mins before expiry_date), you'd call the refresh token API.
This is a method that I've used. However, it may not considered as a best practice.

Categories

Resources