Google API usage with OAuth2

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.

I’m not sure how the API expects it, but have you tried just adding the metadataHeaders multiple times.

format: metadata
metadataHeaders: Message-ID
metadataHeaders: from

Sometimes the brackets are also needed.

format: metadata
metadataHeaders[]: Message-ID
metadataHeaders[]: from

Yes, I tried that too and it throws an error unfortunately. The sample I found from stack overflow of a working version was

message2 =gmail_service.users().messages().get(userId='me', id=thread['id'], format='metadata', metadataHeaders=['subject','from','to','date']).execute()
for num in range(0,4):
  if message2['payload']['headers'][num]['name'].lower() == "subject":
    subject=message2['payload']['headers'][num]['value']
  elif  message2['payload']['headers'][num]['name'].lower() == "from": 
    From=message2['payload']['headers'][num]['value']
  elif  message2['payload']['headers'][num]['name'].lower() == "to": 
    to=message2['payload']['headers'][num]['value']
  elif  message2['payload']['headers'][num]['name'].lower() == "date": 
    date=message2['payload']['headers'][num]['value']

f.write("Date : %s "  % date.encode('utf8')+'\n')
f.write("Subject : %s "  % subject.encode('utf8')+'\n')
f.write("From : %s "  % From.encode('utf8')+'\n')
f.write("To : %s "  % to.encode('utf8')+'\n')

Obviously that persons code does more than just that, but in line 1 you can see how he sent the headers to the API.
I have also tried adding a setValue step as an Array and then passing that into the value, but no luck sadly.

The querystring should become format=metadata&metadataHeaders=Message-ID&metadataHeaders=from, so having multiple metadataHeaders.

Seems that the api action doesn’t support having multiple query parameters with the same name. You could try:

format: metadata
metadataHeaders[0]: Message-ID
metadataHeaders[1]: from

Maybe google supports that format.

Unfortunately, Google doesn’t understand that, it looks like whenever they do not understand something like that they just give the full results instead, although with some of my attempts I have actually got them to produce an error.

Even though it did not work, I have to congratulate you for coming up with the one way I actually never tried though, when I asked the original question I honestly thought I had tried every variation, haha.

alternative you could just put the query directly in the url, that should work

https://www.googleapis.com/gmail/v1/users/userId/messages/id?format=metadata&metadataHeaders=Message-ID&metadataHeaders=from
2 Likes

If I could give that 5 hearts I would, that works like a charm.
I had tried it but not like you did, I tried this originally

https://www.googleapis.com/gmail/v1/users/userId/messages/id?format=metadata&metadataHeaders=["Message-ID","from"]

and this

https://www.googleapis.com/gmail/v1/users/userId/messages/id?format%3Dmetadata%26metadataHeaders%3D%5B%22Message-ID%22%2C%22from%22%5D

I understand a little more of how they expect it now.
THAAAANK YOU

1 Like

I have this app finally working pretty well at the moment, only I am still fighting with being unauthenticated after the access token expires after 1 hour. Is there a preferred way to get the refresh token from the session and send it to Google for them to renew my access token without the user having to interact at all?

Sorry @patrick but i think this will be one of my last little stumbling blocks with this project.

I haven’t set this up using Wappler yet, but I usually create a table to store the access token, refresh token, scope, and expires in date/time. These are provided when the oAuth process returns the user to your app after sign in.

This is from Bubble, but hopefully helps. This workflow runs when Google redirects the user back to the specified redirect URL on my app.

Step 2 grabs the access_token, expires_in, refresh_token, and scope and inserts them into a record in a table.

Step 4 creates a scheduled job to run 120 seconds before the expires date/time to request a new access token. This scheduled job will provide the Refresh token to another workflow.

The workflow it calls, makes a call to https://www.googleapis.com/oauth2/v4/token with the following parameters and will return a new access_token and expires_in for you to store. You then need to setup a new scheduled job to run before the new expires_in time to repeat the process.

https://www.googleapis.com/oauth2/v4/token?refresh_token=[refresh_token]&client_id=[client_id]&client_secret=[client_secret]&grant_type=refresh_token

This is typically how you keep your access token up-to-date for your app to use, and does not require your user to reauthenticate.

Shoutout to @mebeingken for helping me with this process long ago. :wink:

1 Like

Thanks so much, thats very helpful indeed, as far as i know from what Patrick has said the Wappler session already stores and manages to access token, refresh token, etc. so although i could easily save this into my own database, I am kind of hoping that i could access the properties of that already stored cookie or session to send the data needed.
This would cut out the entire step of saving and updating the database, but not too sure if it can work that way.
I think @mebeingken did find a way to access the properties from the Wappler Cookie?

1 Like

Long ago indeed! I’m in the middle of dealing with tens of millions of db records and cringe at the thought of attempting in the bubble. :anguished:

1 Like