What is the best approach for storing OAuth2 tokens?

@psweb, I had a look at your workflow, and I don’t think you need the refresh step “apiGetRefreshToken”, the step that follows (calling the provider) will automatically check the expiry of the access_token and automatically use the refresh_token to fetch a new access_token. @sitestreet

You don’t really need the if else within the box:

Also, there might be a security flaw here… lets say, you login with ur email address, token fetched, persisted in the database… and you closed ur browser…

I, now, open my browser and used your email address, your flow will let me in with ur token, isn’t it? Sorry i could be wrong, Im not a techie, so might miss some fundamentals here!

I understand when token handling is using “Session”, automatic refreshing of token makes sense as it will be for the current user, but when token handling is using “Self-Maintain”, I don’t understand how this will work.

Yes, this script does not run on page load, I have an action scheduler on the dashboard that checks the expiry time every 5 minutes and if the expiry time is less than 5 minutes away from expiry then it runs the “apiGetRefreshToken” script to make sure the user remains logged in.

So my real workflow for this is

  • Login with email address (token-handling-login.php)
    • Check database for matching email address
    • Condition (if Email Exists)
      • THEN get refresh token, pass to self maintain non linked provider, update db record, redirect to dashboard.
      • ELSE use non linked session provider, authorize, insert db record, redirect to dashboard

The silly part with this script is it will get a refreshed access token even if they decide to go back to the login screen while they are still using a already valid access token and it will get a new one anyway. I can not see many people doing that but even if they did the one extra API call is not too terrible so I did not bother.

Now the user is redirected to the dashboard, the page auto runs a script to display some information.

  • Display Info (display-info.php)
    • Query database to get token
    • Add queried result to provider
    • API Action to retrieve profile information

While on this dashboard page the Action Scheduler is running a simple database query to check the expiry time vs the real time and when there is only 5 minutes left on the access token expiry it runs the refresh token script

  • Refresh token (token-handling-refresh.php)
    • Db query to get refresh token
    • API action refreshes the access token
    • Adds the new information into a “linked” self maintain provider
    • Updates the db record with the new information

And that is really it, the login runs, then displays info, and every 55 minutes refreshes the token.

1 Like

You are quite right, I am still in a very developmental stage of this right now, so in the real app, the user will first create an account with security, and then access their various Google accounts from within the secure area, where they can manage multiple gmail email accounts to remove duplication and merge 2 email accounts into one.

As I say, still early days, lots of stuff to work out when i finally put all the parts together.

1 Like

This is such an excellent thread and I’m sure will be really helpful to everyone grappling with OAuth2 and APIs generally. :slight_smile:

2 Likes

@Akayy and @sitestreet, just out of interest with the APIs you are both connecting to does the refresh token get sent more than once, i mean if you do another authorise then does it send a new or existing refresh token to be used.
If you would not mind checking with something like this added to your code i would be grateful. Just want to see whats stored inside the session.

<?php
	// Start the session
	session_start();

	// Show session variables
	print_r($_SESSION); 
?>

The reason I ask is this. As I have said before with Google OAuth, it sends a refresh token only once, and never ever again, you can use providers and authorize steps etc. but you never get a new refresh token.

So I did a test just using session storage. The Access token expires after 3600 seconds (1 hour)
I set a very simple login with an OAuth2 Provider and an OAuth2 Authorise step which redirects me to a simple dashboard page showing a little retrieved data from the API.

I then waited and every 10 minutes or so refreshed the dashboard page, after an hour I continued refreshing and saw the session was updated with a new access token and still had the same refresh token as before, i continued running this for 3 hours to fully test and all worked perfectly.

The I opened a new browser Safari instead of Chrome, and went through the same login process, however this time the Google Account was already authorized to use the App from the above Chrome test.
So it ran fine and gave me an access token only, the refresh token was empty, which is what i expected, I refreshed every 10 minutes again and it ran perfectly for the first hour, however when it needed to handle the auto refresh, it could not as it had no refresh token to send to get the new access token.

So my conclusion is that if you are using an API that only sends a refresh token once in a lifetime, then you would have to use a database to store that token, and you would have to self maintain it, or your access token refreshing will only ever work the first time the user works on the site, and if they use it a couple days later they are going to not see their data after an hour without having to authorize each hour.

1 Like

Hi @psweb. My thinking at the moment is that the refresh token given by Xero has no expiry so I am storing it in the database and will use it to get a new access token when the current one has expired (theirs has a 30 minute expiry time) but I’ve not yet implemented all that. I’m aiming to crack through it today so I’ll happily put your code in and confirm my findings later.

1 Like

I checked at my end, I get a new refresh_token every time authorize runs… haven’t tested auto-refresh yet, I haven’t got that far, just clear on the theory at the moment, will implement and test it out in the next day or two, will share my results.

What if you re-did this test back in chrome (may be in a separate incognito window)? Do you get only access token?

Even if the authorize didn’t send a refresh token, you would have persisted the first refresh_token for the user and feeding this in Provider settings as yours is self-maintain token handling, so refresh should automatically work.

1 Like

Yes, only the access token, just looked now, and the refresh token is no longer there.

Yes, that is exactly why I opted to use the self maintain, however this was just a test to see if I could use the session provider exclusively rather than any self maintain providers, which I cant, I am kind of forced to use self maintain providers with the way the Google OAuth API works.

So in my mind the basic difference is this
If your particular API you are using only provides a non expiring refresh token at birth and never ever again then you would have to use a database with self maintain provider.

If your particular API you are using provides an expiring or non expiring refresh token at birth as well as on authorize then you could probably skip the entire database and self maintain provider and use the simpler session provider.

2 Likes

Excellent summary… Highlighted!

2 Likes

I’m pleased to say that I have it all working as expected. Don’t forget I’m using the Xero API and not Google. This is the structure of my authorise server action file:

You’ll see some inactive Action Steps - they are there for my testing so is nice to just leave there commented out should I need to go back to it.

This is run whenever any API calls fail - I’ve put in an ‘Unauthorized’ Dynamic Event for the Server Connect AC component:

browser1.goto('/dmxConnect/api/Xero/authorise.php')

This can probably be fine-tuned further but it’s working and I’m happy :slight_smile:

Thanks again @Akayy and @psweb for all your help.

2 Likes

Well done @sitestreet, referring to @psweb’s query, does Xero API sends refresh_token only once, and only the access_token on future authorizations?

I’m still not completely sure on this.

It seems that when the Access Token is renewed, a new Refresh Token is also sent. However, it also seems that an old Refresh Token still works and gets the new Access Token. I will try and do some more research on that.

1 Like

Great news @sitestreet, well done.

Quite interesting how different OAuth APIs all work so differently, if Google OAuth could have kept handing the refresh token it would have made life far simpler, seems kind of silly that they don’t in my opinion, but I just assume it’s a security thing or something. Maybe they took lessons of Apple and are just being difficult though, which is also a possibility.

You mentioned about using the unauthorised event, which I also attempted to use, and it does work, it redirects as soon as it’s no longer authorised, I only wish there were another even like almost_unauthorised, so it could run my refresh access token script a few seconds before fully unauthorised, which would then give the user a more seamless experience instead of the browser suddenly redirecting them to an authorise script.
I got around the limitation by using the Action Scheduler however it does have some limitations, which is why i have to check every 5 minutes incase the timer resets from a user interaction.

Anyway, happy it is working and hopefully you can post a little Xero API guide when you have it all worked out.

1 Like

Yep, it’s all working nicely although I have hit an issue which I’m currently looking into but it’s Xero specific, I think, and not OAuth2 generally. Once I’ve sorted that (it cropped up once the client tested it) I will try to write an easy to follow how-to but I need to be sure I’ve understood everything correctly as I’d hate to post something and it not be right, even if it does work.

I completely agree about the unauthorized event not being graceful. Bouncing to a script and then returning isn’t ideal. I’m wondering if it could open a new tab or something and then close it but that’s for another day - if it’s even possible.

One possible solution is to run the authorise script on the main app screen and set the redirect to that same screen. Then, every time the main screen is visited, the check is made and refreshed if needed. That might make it less visible.

2 Likes

Can’t we use the catch on server-side for the unauthorised event triggering? Haven’t tried it myself, will let you know if I had any luck

Is it okay to store tokens locally on device for mobile development?
https://cordova.apache.org/docs/en/latest/cordova/storage/storage.html

Hello @sitestreet, this thread has been extremely useful for understanding the set up of the API for Xero - thank you.

In researching how to develop this for my application I have read another thread which comments that it is not possible to configure Oauth2 token handling as ‘Self maintain’ within the structure of the new Global Server Actions because a database query can’t be run before the Global Oauth Provider (Oauth2 with Salesforce) and the Oauth Provider can’t be set up inside an action. I haven’t found a way to solve this and searching the Wappler forum hasn’t shown up any solutions either.

Is this a problem you have encountered or has it not come up for you because your Xero API was developed before the Global Server Actions were released? If you have had this problem can you say how you got around it?

Hi @Marek. I haven’t come up against this probably because I did the Xero integration before the global actions were introduced. I will keep this in mind the next time I’m doing something like this, though, but I’m afraid it’s not particularly helpful to you right now. Sorry :frowning:

That said, I have made edits to the site which used Xero since globals were introduced and I did replace the actions for globals (database connection, security and mail) and didn’t have any issues.

1 Like

Hi @sitestreet,
Any chance of a xero integration walkthrough/guide when you get a moment? :+1: