Google API usage with OAuth2

I have been trying to access the Google API with Wappler, and have not really found it to be doing quite what I would expect.
Take an example of using the gmail.api with regular old PHP, which Google pretty much provides the libraries to install all the supporting methods, classes and functions.

In PHP your app would run, it would first look for a file on the webserver called token.json then read the contents of that file, checking the created timestamp against the expires in time and if expired, use the refresh_token to get a newer access_token

This file looks like this but with far far longer codes for access and refresh.

{"access_token":"ya29.a0Adw1xeXt???????","expires_in":3599,"refresh_token":"1\/\/03G7pgZAUTLBXCgYIARA??????","scope":"https:\/\/www.googleapis.com\/auth\/gmail.readonly","token_type":"Bearer","created":1585475522}

If however the file does not exist then it creates a new one for you and uses the access_token part of that to make sure the person does not have to keep authenticating their google account.

I have tried to replicate the functionality with little success, as I can not seem to get the refresh_token from Google firstly and secondly I can not find a way to write the data I do have into token.json file. I only have options for csv or xml file output which is not quite producing the results I am needing.

I also know that in my next step if I do get the export working as needed, I will also need to figure out how to read the contents of the .json file later on to make calculations against the created timestamp and the expires_in field, which I have not got to as yet but do not think it would be possible.

This Auth as far as I am aware is the same for all the APIs in the Google product offering, so I assume I am just overthinking things or something as I have not read of other people experiencing the same issue as yet. @patrick maybe you could try fix my thinking here.

Keep in mind that the refresh token is only provided on the first authorization, so during development and testing you often need to revoke permission. This can be done here: https://myaccount.google.com/permissions

3 Likes

Maybe this will help. Here is how I do auth for a web app that uses Google accounts for authorization.

I’ve stripped a bunch away where I do domain matching, etc.so you can focus on the oauth2.

First I use the Wappler Oauth2

And then I do use the Google php client to verify the token. I am using an api action to call a local php file:

Here is the contents of that file:

<?php

// The google app client id
$client_id = "YOUR-CLIENT-ID";

// The token returned by a successful google login
$id_token = $_POST["idtoken"];

// setup a google client
$client = new Google_Client(['client_id' => $client_id]);  // Specify the CLIENT_ID of the app that accesses the backend

// perform the token validation
$payload = $client->verifyIdToken($id_token);

// if a valid token and the clientid still matches 
if ($payload && $client_id == $payload['aud']) {
  	
  	//build the json parameters
	$data = array('sub' => $payload['sub'], 'email' => $payload['email'], 'given_name' => $payload['given_name'], 'family_name' => $payload['family_name']);
 	$json_payload = json_encode($data);
 
  	// send the result back to calling entity
    echo $json_payload;
  
} else {
  
  // invalid login
  echo null;

}

?>
1 Like

Hmm I don’t think you need an additional php file for this, maybe @patrick can advise

2 Likes

I always wondered if you guys were already handling this!

1 Like

They are everywhere :slight_smile:

1 Like

Thanks Ken, very handy information, really appreciate it.

Could you try explain a little more about how the token.json file actually works, because I found that part pretty confusing.
I will have a number of users for this that could be using this at the same time, so what I am confused about is how a single token.json file that gets generated is going to work, i mean if another user logs in to a different gmail account then the token.json file already on my server which is perfect for user A is going to be overwritten by user B.
It just makes no sense to me at all im afraid, i would imagine each person that logs in will need their own token.json file unique to them or the single token.json file will need to have multiple entries relative to the user account?

Also I have looked through your screenshots and mine are pretty similar to yours, but obviously for a different api, what particular api interface is yours for?

And lastly to try clarify this refresh token thing.
I was reading about some limit of 50 refresh tokens per user account or something, so if you have more info on what or how these refresh tokens are meant to work in laymans terms I would be grateful.

1 Like

I don’t really deal with any token.json file – nothing in my flow writes or reads files, it just reads data from the api calls.

The scopes I use are simply to perform a login. This is an internal portal for a company that does as much as possible using Google. So this just allows them to login to the portal with their existing Google account credentials. I also grab some basic information during the process and check the domain to make sure they are valid users of the company.

There are two tokens from Google – access and refresh.

Refresh is given to you only when first authorization, and should be stored securely for the life of the account. Refresh tokens will not provide access.

The access token is a limited duration token that allows access to resources as specified in the scopes.

The refresh token is sent to Google which in return provides a valid access token that expires.

When one is manually managing this (as opposed to a wrapper like the Wappler oauth2 actions) you simply store the expiration time, and using the refresh token ask Google for another access token.

The 50 refresh token limit is per user, not per client_id. This is why you want to store the refresh token so you don’t ever have to deal with this. 50 is plenty and for most use cases, you just need one.

1 Like

Thanks so much, that really does help a lot, i feel like I have spent 4 days reading the entire oxford dictionary and trying to get it all locked down in my head.
Obviously with using certain programming languages that Google feels are widely spread enough, it has made it far easier to a degree in the past with plain PHP because i never really needed to understand each part, but rather just use their provided classes and read the docs on each class to make whatever i want work.

However i think it would be pretty interesting to get it working correctly inside Wappler, considering so far, i literally have a single action file with 4 steps and can already read gmail labels with it, thats quite impressive from Wapplers side, as the same thing using Google classes is almost a page of PHP code and 7 classes to get to the same point im at now, so this is looking far far more simplified than that.

Hopefully Patrick can try help me understand why the standard google clases all use this token.json file for their authentication methods, because to be honest it seems like a database table would be a far simpler solution to store that data over this json file idea.

I mean if your app is not dealing with the token.json file thing at all, and your users are managing to remain logged in without it, then maybe i also do not really need to worry about it as much.

EDIT: Just a little update, I waited an hour for my token to expire and sure enough with the standard oauth2 from Wappler the refresh_token was handed over from Google, so it seems i have everything i require as per what is in the standard token.json file, however I am instead going to use it inside a database so i do not have to try write this to a file and even more so I do not have to try read back the parameters from a file.

The Authorize step returns access_token, refresh_token and expires_in. Our provider automaticly does the refresh for you, default it stores the refresh token and access token in a session and uses the refresh token to generate a new access token when needed. You can change this in the provider properties, you could for example store the refresh_token and access_token in your database and then set the token handling to Self Maintain to use the tokens you have in your database.

3 Likes

Sounds brilliant Patrick, so if your provider already stores the token information into a session, can I access its properties through Server Connect.

Maybe I am wrong, but I assume I need some type of condition setup as the server connect steps currently are
OAuth2 Provider
OAuth2 Authorize
Which are two steps that do not need to run if the user is already authorised I assume, currently my application is running those 2 steps each time the user needs to do something.

Is there a condition I can use that says if the tokens are already fine then skip those 2 steps in the process and just fetch the data from the API?

The authorize step is only needed to authorize the user once, it is the login step. Default the tokens are stores in a session and you only need to include the provider step with all the api calls.

Depending on the service the refresh_token can be used for a very long time, so you could store the tokens in your database with the user, then the next time the user visits the site you use the tokens from the database and the user doesn’t need to authorize again.

You can skip the authorize step if the provider returns an access_token, then it already was authorized.

1 Like

Ok perfect, I think I got it.

So I should land up with my action file looking something like this then.

This is why every question I ask is generally aimed at “What am I doing wrong” because 99% of the time you guys have already worked this stuff out, and I am just not using it right.

I would create 2 separate actions, you just do your api actions in one action file. The api action would probably return an unauthorized error or you can just check the access token yourself and return a 401 status when it is not available. On the server connect component you put a dynamic unauthorized event and there you do a browser goto action to the authorize action file.

The authorize step does a redirect, that will not work in a ajax call, that is why you will need a browser redirect to it. After the authorize you put a redirect step to go back to the original page.

1 Like

Sorry to be a pain Patrick, but is there any chance you could try show this as a screenshot or something in server connect.

It seemed pretty simple on the surface however to have a server action with only the API steps and no OAuth Provider nor Auth and another action with just the provider and auth.

The issue I run into though is i can not choose the OAuth2 provider within the API action files even though i have hit the global link icon on my Provider step, so im pretty stuck with this solution.

Here is a rough idea of what I am trying to get in the end of the day.
I would like a webpage split into a left side and a right side. On the left I would like a listing of the email from gmail account A and on the right another email listing of email from gmail account B.
The idea behind this is that my client has multiple gmail accounts that they want to try filter out all the email duplication between the accounts, so if I can just get the listing part so far it would be a great win, keeping in mind i want to display the contents of 2 different gmail accounts side by side.

The authorize action is needed to authorize the user and to get the actual access token. The user is redirected to google to login and authorize your app to access their account. After that you redirect the user back to your page.

image

You can link to this action page like <a href="dmxConnect/api/authorize.php">Authorize</a> or redirect to it using the browser component. You should not call this action with a server connect component.

For the API call, to get for example the user profile or in your case the gmail listings, you use the same oauth provider again and in your api call you choose authorization OAuth2 and select the provider.

image

image

Alternative you could do the authorize action in the unauthorized event of your server connect component, if the user was authorized the server connect would return the api response, otherwise the user is redirected to google.

image

Also it is possible to do the logic inside a flow

image

3 Likes

Thank you 100 times over Patrick, I really appreciate that, going to give it another go and see what I can get.

Paul actually the Google oauth2 workflow is about the same as the facebook one.

Maybe you missed the tutorial:

1 Like

I wish I had missed that one George, I have basically been using that as my bible as I try do this, however I just think I am missing something essential along the way somewhere.

I am going to test out this idea and see how I go, and then if I don’t quite get it right I will try explain in more detail whats not working for me, however I think the issue here is I am not just trying to do a login and compare the data all server side, but rather trying to display some of the returned data from the server side to the client side. Basically if it was not for CORS, i think this would have been pretty simple.

EDIT: So far so good, this is the furtherest I have managed to get, will keep you all updated.

I know its not completely relevant to this thread but still part of the Google API questions, so I figure I will ask this here.

There are some calls within the Google API that allow for the query to take a list, such as.


To see the full page https://developers.google.com/gmail/api/v1/reference/users/messages/get
However when trying to add the query into the Query section in Wappler it only works when I give it a single value and not a list of values.
So this works

but this does not

I have tried a number of ways to add the list with/without quotes, square brackets, single quotes, comma separated, colon, semi colon, etc. but can not seem to find the correct way in the UI. I even tried URL encoding the list first, but I am out of ideas.

I did note the spelling mistake of Message/Massage, but it was spelt correctly in real world tests.

And just a thank you because this Google API project I am working on is going very well thanks to the patience of our Wappler team, really appreciate that.