Google API usage with OAuth2

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

Seems like there are generally two use cases here. Doing things on behalf of a user when they are actively engaged online, and doing things for users when they are offline.

Wappler oauth2 action with sessions for the former and Wappler oauth2 action with database for the latter.

If you try and use session when the user is offline, I would think you have no way of knowing which session you want.

2 Likes

Well I think I have finally got the Auth sorted out on my Google App.

I must ask though, what I have quoted above makes it sound like it should have been far simpler than it was, therefore I assume, I have possibly jumped through a few hoops incorrectly.

So I have an OAuth Provider step and an OAuth2 Authorise step and a redirect step all in a single server action, it does a great job here.

The user clicks a button on my home page, it opens a google account chooser page, they click the account they want, it moans about an un authorised app, which is expected, they say ok, they accept the permissions from google and now i land up on my main page where all their data is displayed.

The data is displayed using a series of api calls, each server action that accesses the API starts with the OAuth2 Provider step, without another Authorise step, because if I do use another authorise step then it takes the user back to the Google Account Chooser page.

So far so good. However after exactly 1 hour, regardless of if I am working on the page or not, the account is logged out, as unauthorised.

The way I read what I quoted above, the provider should have automatically handled the refresh for me and therefore even after 1 hour the user should not be logged out and have to go through the authorise step again.

Sorry to harp on and on about this but I just want to make sure I am understanding this all correctly.