1

I'm building a system which people can authenticate to read their gmail. I've got it working perfectly well on my website using OAuth2 in Python; in the response from google I get an access_token, a token_type, an expires_in and (most importantly) a refresh_token.

I now want to get the same working in Javascript (for an app I'm building using Ionic). So I use ngCordovaOauth to authenticate with the following code:

$cordovaOauth.google(
    'xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
    ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/gmail.readonly'],
    {
        'access_type': 'offline',
        'approval_prompt': 'force'
    }
)
    .then(function(result){
        console.log("Response object -> " + JSON.stringify(result));
    }, function(error){
        console.log('Error -> ' + error);
    });

As expected, this produces the following screen on my phone:

enter image description here

But in the json I get back there is no refresh_token:

{"access_token":"xxxx.xxxxxxxxxxxx","token_type":"Bearer","expires_in":"3599"}

Does anybody know why I don't get the refesh_token when using the Javascript api? All tips are welcome!

kramer65
  • 50,427
  • 120
  • 308
  • 488

2 Answers2

0

CordovaOauth is using the Implicit grant type of OAuth 2.0, which per the specification is meant for "clients implemented in a browser using a scripting language such as JavaScript". With this flow the client is directly issued an Access Token, and only that.

In the Authorization Code grant type, the client is issued an Authorization Code, which it then exchanges for Access and Refresh Tokens, but this also includes the authentication of the client itself, which is why it's done in a Server-to-Server manner. You wouldn't want to expose your client_id/client_secret in the end-user facing JavaScript code.

Please also have a look at the Google Documentation explaining the different scenarios.

Community
  • 1
  • 1
Mehmet Y.
  • 460
  • 1
  • 3
  • 10
  • Thank you for the clarification. So do I understand it correctly that there is no way for me to get the refresh_token from within my Ionic/Angular-app? – kramer65 Mar 23 '15 at 12:50
  • While I don't know much about Ionic, a server-to-server communication will be needed in order to use the Authorization Code grant type which will cause a Refresh Token to be issued (if requested). So a pure JavaScript solution running in the user's browser won't do. – Mehmet Y. Mar 23 '15 at 13:05
  • Okay, so here's my new idea: Don't use an implicit grant type so that I can actually get the Authorization code in javascript. Then I send the obtained authorization code to my server and use my server to obtain the access token and the refresh token. Does that make sense to you? – kramer65 Mar 23 '15 at 13:10
  • In theory, yes. You probably won't be able to use CordovaOauth, at least not without modifications (careful!). – Mehmet Y. Mar 23 '15 at 13:19
  • "In theory, yes.".. Is it also a common route with apps written in js, or would you suggest a different route or technique? – kramer65 Mar 23 '15 at 13:27
  • In a way you'll initially be doing what you've previously done using Python, so you might want to consider leveraging that. As I said before, the Implicit grant type is meant for JS, which shouldn't require any offline access anyway (or am I wrong?). This very much depends on your application though, so I don't think there's much else I can add. – Mehmet Y. Mar 23 '15 at 13:49
0

Why shouldn't you be able to request a refresh token with Cordova/Ionic/Angular? ...

I have an application which gets a refresh-token, access-token etc. Therefore i used this code:

apiKey                  = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
clientID                = 'xxxxxxxxxxxxxxxxxxxxxxxxxx2ivu3.apps.googleusercontent.com';
var notiID              = 0;
var googleapi           = {

authorize:  function(options) {
                var deferred = $.Deferred();
                var authUrl = 'https://accounts.google.com/o/oauth2/auth?' + $.param({
                    client_id: options.client_id,
                    redirect_uri: options.redirect_uri,
                    response_type: 'code',
                    scope: options.scope
                });

                var authWindow = window.open(authUrl, '_blank', 'location=yes');
                $(authWindow).on('loadstart', function(e) {
                    var url = e.originalEvent.url;
                    var code = /\?code=(.+)$/.exec(url);
                    var error = /\?error=(.+)$/.exec(url);

                    if (code || error) {
                        //Always close the browser when match is found
                        authWindow.close();
                        loadingSpinner();
                    }

                    if (code) {
                        $.post('https://accounts.google.com/o/oauth2/token', {
                            code: code[1],
                            client_id: options.client_id,
                            client_secret: options.client_secret,
                            redirect_uri: options.redirect_uri,
                            grant_type: 'authorization_code'
                        }).done(function(data) {
                            deferred.resolve(data);

                            $("#loginStatus").html('Name: ' + data.given_name);
                        }).fail(function(response) {
                            deferred.reject(response.responseJSON);
                        });
                    } else if (error) {
                        deferred.reject({
                            error: error[1]
                        });
                    }
                });

                return deferred.promise();
            }
        };

And this one for requesting acces and refresh-Token:

function callGoogle()
{

//  alert('starting');
    googleapi.authorize({
                        client_id: clientID,
                        client_secret: apiKey,
                        redirect_uri: 'http://localhost',
                        scope: 'https://www.googleapis.com/auth/analytics.readonly'
                        }).done(function(data) {
                                localStorage.accessToken=data.access_token;
                                localStorage.refreshToken=data.refresh_token;
                                // console.log(JSON.stringify(data));
                                // console.log(localStorage.accessToken);
                                // console.log(localStorage.refreshToken);
                                getDataProfile();


                                });

}

Give it a try and let me know, where u need further help. You will only need the inappbrowser and console plugin for that. Also of course jQuery.

Sithys
  • 3,655
  • 8
  • 32
  • 67
  • Thank you for your extensive answer. May I ask though: is it safe to provide your apiKey and clientId in the js source of an app? – kramer65 Mar 23 '15 at 14:03
  • Look at this thread -> http://stackoverflow.com/questions/28276712/cordova-google-maps-api-key-safe-enough There should be enough information for you :-). – Sithys Mar 23 '15 at 14:05
  • Sorry kramer, that was the wrong link i posted, i meant this one - 3 days ago -> http://stackoverflow.com/questions/29157626/how-to-secure-my-google-maps-javascript-v3-api-key-within-an-ionic-cordova-app – Sithys Mar 23 '15 at 14:07