1

I have a mobile app and I want to add events on the user's google calendar from my server. I've added the consent flow on iOs and I get an idToken from the response. I can also get an accessToken using getTokens on the client. However I am not able to use these in order to get an appropriate refresh token on the server so I can call the Calendar API at a later stage. For a web app it requires a client secret. The ios client does not have a secret and when I use my web client id and secret I get insufficientPermissions: Insufficient Permission: Request had insufficient authentication scopes. .

To clarify, my steps are:

  1. get consent and idToken on the phone
  2. send idToken to the server
  3. use idToken to get refresh token on the server (this is where things fall apart)
  4. use refresh token to create events on the user's calendar

Here's my ios React Native code:

signInWithGoogle() {
    GoogleSignin.configure({scopes: ['https://www.googleapis.com/auth/calendar', 'https://www.googleapis.com/auth/calendar.events']});

    GoogleSignin.signIn()
    .then(result => {
      // idToken
      console.log(result);
      GoogleSignin.getTokens()
      .then(tokens => {
        //this gets me an accessToken
        console.log(tokens);
      })
    }).catch(error => {
      console.log('google auth error: ', error);
    });
  }

And my server side code:

secrets = Google::APIClient::ClientSecrets.new({
      "web" => {
        "refresh_token" => ENV['GOOGLE_REFRESH_TOKEN'],
        "client_id" => ENV["GOOGLE_CLIENT_ID"],
        "client_secret" => ENV["GOOGLE_CLIENT_SECRET"]
      }
    })
auth_code = idToken_from_client
auth_client = secrets.to_authorization
auth_client.update!(scope: 'https://www.googleapis.com/auth/calendar.events')
auth_client.code = auth_code
auth_client.fetch_access_token!

I know the server code doesn't make sense because I already have an access token, however I need a refresh token. I will need to create events for the user as long as they use the app and access tokens expire. And this is what they recommended for getting a refresh token in the server side auth docs: https://developers.google.com/identity/protocols/oauth2/web-server#exchange-authorization-code

Thanks for your help!

Tamar
  • 2,016
  • 1
  • 15
  • 26
  • 1
    Please edit your question and include your code. YOu dont need a refresh token to access the api you need an access toekn. It sounds like you have not asked for the proper scope when authenticating your user. You can use Ruby-on-rails in IOs? – Linda Lawton - DaImTo Jun 03 '20 at 11:51
  • @DaImTo I've added code and explained why I need a refresh token, not an access token, so it does not expire and I don't have to repeatedly ask the user for consent – Tamar Jun 03 '20 at 12:54
  • I would still like to know what Ruby-on-rails has to do with your question and why it is tagged as such if you are doing IOs. To my knowledge mobile authorization does not use a refresh token. Also you cant mix React authorization with what ever you are doing on server side the client ides are different the authorization types are diffrent – Linda Lawton - DaImTo Jun 03 '20 at 13:03
  • @DaImTo I have a client side where I do the authorization but the information for creating events is on the server side. I am looking for a way to use the token generated from the client side with the google-api-ruby-client gem to obtain a refresh token. Is it not possible? – Tamar Jun 03 '20 at 14:12

2 Answers2

0

What I ended up doing is using the auth flow for installed apps opening a system browser window, using the web client id and secret. I use the redirect uri pointing to my server endpoint where I use the google-api-client to obtain a refresh and access token with the provided code.

ReactNative code:

const scope = 'scope=the_google_scope';
const redirect = 'redirect_uri=redirect_for_google_auth'; // Your serverside endpoint
const url = `https://accounts.google.com/o/oauth2/v2/auth?${scope}&prompt=consent&access_type=offline&response_type=code&${redirect}&client_id=${googleClientID}`

Linking.openURL(url);

Rails code:

require "google/api_client/client_secrets.rb"
...
secrets = Google::APIClient::ClientSecrets.new({
      web: {
        client_id: googleClientID, # Same one used on the client
        client_secret: googleClientSecret,
        grant_type: "authorization_code",
        redirect_uri: "redirect_for_google_auth"
      }
    })
auth_client = secrets.to_authorization
auth_client.code = code_retrieved_from_google
auth_result = auth_client.fetch_access_token!

Hopes this helps other people with this scenario.

Tamar
  • 2,016
  • 1
  • 15
  • 26
  • 1
    Redirect URI on react native and on rails code both are same? – Amit Tiwary Aug 07 '20 at 06:27
  • @AmitTiwary yes, they must be or you will get an error – Tamar Aug 07 '20 at 07:23
  • Then how it works? In react, native redirect URI is the endpoint on the server so after so after user permission it will redirect the user to the server and then again on the server you are asking calendar permission. I am not able to understand the flow when the user clicks on a button for calendar access then what happens and how you are communication between client-side and server-side. – Amit Tiwary Aug 07 '20 at 08:27
  • @AmitTiwary the server side call does *not* redirect, you will get a response back. However you must include the redirect url and it must match, or at least be one of the authorized redirect urls you have set on these credentials on the google cloud platform credentials. – Tamar Aug 07 '20 at 20:02
  • @Tamar I have the same problem. Is the following procedure true? First, you created a web application credentials in google developer console, then used the `client_id` in both react native as well as backend side, next you created the `/oath2callback` endpoint in the backend and used it as `redirect_uri` in the native side? – Benyamin Jafari Jul 23 '22 at 16:56
  • @BenyaminJafari I actually did a talk with the full procedure - it's based on what's written here with some additional info and pointers: https://www.youtube.com/watch?v=a9SHdWLnqn8 – Tamar Sep 28 '22 at 09:47
0

Because you are following the wrong flow. When you are following an installed app flow you can reach the access_token and referesh_token from the mobile side but it doesn't work on the server-side. So, you need to pursue the web flow instead which your mobile app just brings up the google oauth2 authorization page and performs sending authorization code toward server-side and this is server-side responsibility to exchange that code to access_token and refresh_token using the web flow app credentials which are created in the google developer console.

Here I've posted a more comprehensive answer to solve this issue.

Benyamin Jafari
  • 27,880
  • 26
  • 135
  • 150
  • 1
    yes, you need the web flow. I found the correct solution eventually and even made a talk out of it :D https://www.youtube.com/watch?v=a9SHdWLnqn8 – Tamar Sep 28 '22 at 09:51
  • 1
    @Tamar Nice. I also came up with this issue eventually and created a demo of our application on youtube for google consent: https://www.youtube.com/watch?v=71BtAQzehqc&t=25s – Benyamin Jafari Sep 28 '22 at 10:30