hung.dev
hung.dev

Reputation: 189

Google Authentication Token return doesn't contain refresh_token

I write an example about google api using. Google NodeJS Client library. I have followed the instruction set access_type : 'offline', however the object return doesn't contains refresh_token.

My Code:

var http = require('http');
var express = require('express');
var Session = require('express-session');
var google = require('googleapis');
var plus = google.plus('v1');
var OAuth2 = google.auth.OAuth2;
const ClientId = "251872680446-rvkcvm5mjn1ps32iabf4i2611hcg086e.apps.googleusercontent.com";
const ClientSecret = "F1qG9fFS-QwcrEfZbT8VmUnx";
const RedirectionUrl = "http://localhost:8081/oauthCallback";

var app = express();
app.use(Session({
    secret: 'raysources-secret-19890913007',
    resave: true,
    saveUninitialized: true
}));

function getOAuthClient () {
    return new OAuth2(ClientId ,  ClientSecret, RedirectionUrl);
}

function getAuthUrl () {
    var oauth2Client = getOAuthClient();
    // generate a url that asks permissions for Google+ and Google Calendar scopes
    var scopes = [
      'https://www.googleapis.com/auth/plus.me'
    ];

    var url = oauth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: scopes // If you only need one scope you can pass it as string
    });

    return url;
}

app.use("/oauthCallback", function (req, res) {
    var oauth2Client = getOAuthClient();
    var session = req.session;
    var code = req.query.code;
    oauth2Client.getToken(code, function(err, tokens) {
        console.log("tokens : ", tokens); 
          // Now tokens contains an access_token and an optional refresh_token. Save them.
          if(!err) {
            oauth2Client.setCredentials(tokens);
            session["tokens"]=tokens;
            res.send(`
                <html>
                <body>
                    <h3>Login successful!!</h3>
                    <a href="/details">Go to details page</a>
                <body>
                <html>
            `);
          }
          else{
            res.send(`
                <html>
                <body>
                    <h3>Login failed!!</h3>
                </body>
                </html>
            `);
          }
    });
});

app.use("/details", function (req, res) {
    var oauth2Client = getOAuthClient();
    oauth2Client.setCredentials(req.session["tokens"]);

    var p = new Promise(function (resolve, reject) {
        plus.people.get({ userId: 'me', auth: oauth2Client }, function(err, response) {
            console.log("response : " , response);
            resolve(response || err);
        });
    }).then(function (data) {
        res.send(`<html><body>
            <img src=${data.image.url} />
            <h3>Hello ${data.displayName}</h3>
            </body>
            </html>
        `);
    })
});

app.use("/", function (req, res) {
    var url = getAuthUrl();
    res.send(`
        <html>
        <body>
<h1>Authentication using google oAuth</h1>
        <a href=${url}>Login</a>
        </body>
        </html>
    `)
});


var port = 8081;
var server = http.createServer(app);
server.listen(port);
server.on('listening', function () {
    console.log(`listening to ${port}`);
});

Upvotes: 4

Views: 1929

Answers (3)

Bertrand Martel
Bertrand Martel

Reputation: 45493

The refresh token is only sent once the first time user login to your application after approving the scopes you have specified.

Edit 08/2018 : Using approval_prompt:'force' no longer works, you need to use prompt:'consent' (check @Alexander's answer)

If you want to get the refresh token each time user login (even if user has already login before and approved the scopes), you have to specify prompt:'consent' in Oauth2Client configuration :

var url = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: scopes,
    prompt : 'consent'
});

Note that this will require user to accept the specified scope each time he/she will click on your link to authenticate :

enter image description here

You can also disable manually the application in your account permission settings, this will revoke the application and the user will have to accept the scopes again that will trigger the refresh_token to be sent the next time you authenticate :

enter image description here

FYI, if you need to use access_token offline, you have to store the refresh_token server side, and refresh the access_token with the stored refresh_token when you receive status 401 from Google API. So, if you store refresh_token as you should, there is actually no need to use prompt:'consent' and force user to approve the scopes each time he/she connects to your application.

Upvotes: 5

Alexander
Alexander

Reputation: 7832

According the documentation the refresh_token is only returned on the first authorization.

You can remove the permissions manually on: https://myaccount.google.com/permissions

Also you can force the user to see the consent screen again by passing &prompt=consent in the authorization URL, just add this parameter:

var url = oauth2Client.generateAuthUrl({
  access_type: 'offline',
  scope: scopes,
  prompt: 'consent'
});

It worked just fine for me :)

Upvotes: 1

Linda Lawton - DaImTo
Linda Lawton - DaImTo

Reputation: 117281

In some cases (Web clients mostly) the refresh token is only sent the first time the user is authenticated.

If you go to apps connected to your account remove the app in question. Then try and authenticated again. Check and there should now be a refresh token.

Upvotes: 0

Related Questions