Success! Google Oauth2 for Corona and Lua

Hi there!

I managed to get Google Oauth2 authentication working!!! Code is added here - in this case the code is for accessing and reading out your private calendars, but you can use this code in adapted form to use with all of Googles good bits! :slight_smile:

For your info: Oauth turned out not to be so hard, but I have to say that all the documentation makes things so ****ing hard and confusing that it took me a while to get to grips with things.

For starters, here’s my code to share (pretty rough code, no error checking etc…), if I find some time I’ll be around to explain a bit further, if needed.

One thing I can tell you is that the Google Oauth2 playground is worth its weight in gold: you can do “live” testing of everything, and see exactly how the code should be formatted. After that it’s just “copy-paste” to modify this code.

local json = require( "json" ) -- Google Test local gClientID = "insert your own client ID here" local gClientSecret = "insert your own client secret here" local gRedirectURI = "insert your own redirect URI here - this may very well be just google.com, as long as you set the same URI in your google application credential settings" local gAuthorizationURL = "https://accounts.google.com/o/oauth2/auth" local gScope = "https://www.googleapis.com/auth/calendar" local gAccessTokenURL = "https://www.googleapis.com/oauth2/v3/token" local gGetCalendarListURL = "https://www.googleapis.com/calendar/v3/users/me/calendarList" local webView = native.newWebView( display.contentCenterX, display.contentCenterY, 600, 600) local authorizationURL = string.format("%s?redirect\_uri=%s&response\_type=%s&client\_id=%s&scope=%s&approval\_prompt=%s&access\_type=%s", gAuthorizationURL, gRedirectURI, "code", gClientID, gScope, "force", "offline") webView:request(authorizationURL) local authorizationCode local gAccessToken local getCalendarListCallback = function(event) print(event.response) local decodedResponse = json.decode(event.response) print(#decodedResponse) end local getCalendarList = function(event) local parameters = {} local headers = {} headers["Authorization"] = "Bearer " .. gAccessToken parameters.headers = headers --parameters.body = string.format() network.request(gGetCalendarListURL, "GET", getCalendarListCallback, parameters) end local requestCredentialsCallback = function(event) print("rccb") print(event.response) local decodedResponse = json.decode(event.response) if decodedResponse["access\_token"] then gAccessToken = decodedResponse["access\_token"] print("access token set") getCalendarList() end end local requestCredentials = function(authorizationCode) local parameters = {} parameters.body = string.format("code=%s&redirect\_uri=%s&client\_id=%s&client\_secret=%s&scope=%s&grant\_type=%s", authorizationCode, gRedirectURI, gClientID, gClientSecret, gScope, "authorization\_code") network.request(gAccessTokenURL, "POST", requestCredentialsCallback, parameters) end local authorizationListener = function(event) if event.type == "loaded" then print(event.url) authorizationCode = string.match( event.url, 'code=(.+)' ) if authorizationCode then requestCredentials(authorizationCode) end end end webView:addEventListener( "urlRequest", authorizationListener )

Already a little info here:

The process for Oauth2 from Lua / Corona is as follows:

  1. You go to Google’s developer site to register your application and define which Oauth APIs you want to use. In return you get a client ID and client secret that you will need. You will also need to enter a “redirect URI” here. This was confusing to me at first, but as it turns out for Corona, it doens’t really matter what you enter here, as long as you use the same exact URI in your app.

  2. You open a webview window (so sort of a browser window) in your Corona app, and point it to a URL address that contains a lot of info in the URL, including your client ID and secret. The exact URL is defined by what service you want to access, and can be found in the Google Oauth2 playground easily.

  3. In this case, the URL goes to a Google form that is opened in the webview, and where you need to give permission to use your calendar data (by entering your google address and password, or if already logged in by simply clicking “allow”).

  4. When your login is correct, Google sends your browser to your “redirect URI”, and adds the Oauth2 authentication code in this process. You set an eventListener that detects when this page is loaded, and when “loaded” you can read out the URL and all the info that was embedded - in this case the important bit it is the Oauth2 authentication code.

  5. Once you have this authentication code, you can do a network request to another URL (all found in Google’s Oauth playground) where you add this auth. code in the request, and in return you get an access and refresh token in the response from Google!

  6. After that you have the most important bit: the access token! Once you have this, you can post requests to Google’s Calendar servers containing info about the specific data or operation you want, including the access tokens, and you well get a response from the server containing data (often in JSON format.).

Voila! That’s it. Most of the work is spelling out the POST request in the right way (using the right URL etc…), setting an eventListener to see when there is a response, and extracting the code you want out of the response.

Cool!

Sweet!  Thanks for posting, Thomas.

You’re welcome! I’m writing a more debugfriendly demo module as we speak, should be ready in an hour!

Here you go: this one’s a LOT better. Debugger integrated, and all the code is in logical order from top to bottom so you can read through and try to understand.

Run this on an iPad, preferably (or the simulator, as you need to read lots of fine text), and it should ask you to log in for your Google account, and in the end give you a listing of all the calendars you use (by email address).

------------------------------ -- main.lua ------------------ ------------------------------ display.setStatusBar( display.HiddenStatusBar ) local json = require( "json" ) local OA2 = {} local debugWindow = display.newGroup() debugWindow.lineCounter = 0 debugWindow.y = 2000 debugWindow.addLine = function(inputA, inputB) if inputA then local textA = display.newText(debugWindow, tostring(inputA), 0,debugWindow.lineCounter, native.systemFont, 32) textA.anchorX = 0 textA.x = 0 if inputB then -- second entry for this line local textB = display.newText(debugWindow, tostring(inputB), 200,debugWindow.lineCounter, native.systemFont, 32) textB.anchorX = 0 textB.x = 300 end end debugWindow.lineCounter = debugWindow.lineCounter + 40 debugWindow.y = debugWindow.y - 40 end -- Google Oauth2 data OA2.clientID = "996708749680-l7bntaouktpptiksm1mf0jp9m13suiji.apps.googleusercontent.com" OA2.clientSecret = "18wjQINlblj-xqHSBGWI4VxP" OA2.redirectURI = "https://www.google.com" OA2.authorizationURL = "https://accounts.google.com/o/oauth2/auth" OA2.scope = "https://www.googleapis.com/auth/calendar" OA2.accessTokenURL = "https://www.googleapis.com/oauth2/v3/token" OA2.getCalendarListURL = "https://www.googleapis.com/calendar/v3/users/me/calendarList" -- create webView OA2.openLoginWebView = function() OA2.formattedURL = string.format("%s?redirect\_uri=%s&response\_type=%s&client\_id=%s&scope=%s&approval\_prompt=%s&access\_type=%s", OA2.authorizationURL, OA2.redirectURI, "code", OA2.clientID, OA2.scope, "force", "offline") OA2.webView = native.newWebView( 768, 750, 1436, 1400) OA2.webView:request(OA2.formattedURL) OA2.webView:addEventListener( "urlRequest", OA2.authorizationListener ) end -- listen to what happens in the webview OA2.authorizationListener = function(event) for k,v in pairs(event) do debugWindow.addLine(k,v) end debugWindow.addLine() -- check to see if the authorization token was maybe handed to us in one of these webview events!! local checkForCode = string.match( event.url, 'code=(.+)' ) if checkForCode then OA2.authorizationCode = checkForCode debugWindow.addLine("GOT CODE!:", OA2.authorizationCode) OA2.webView:removeEventListener( "urlRequest", OA2.authorizationListener ) OA2.webView:removeSelf() OA2.requestTokens() end end -- once we have the authorizationCode we're going to send this code to Google to receive the OAuth2 access token and refresh token in return! OA2.requestTokens = function() local parameters = {} parameters.body = string.format("code=%s&redirect\_uri=%s&client\_id=%s&client\_secret=%s&scope=%s&grant\_type=%s", OA2.authorizationCode, OA2.redirectURI, OA2.clientID, OA2.clientSecret, OA2.scope, "authorization\_code") network.request(OA2.accessTokenURL, "POST", OA2.requestTokensCallback, parameters) debugWindow.addLine("Authorization code sent to Google in return for access and refresh tokens") end -- here we monitor the response we get to the token request, and see if there is an acces token inthere somewhere! OA2.requestTokensCallback = function(event) if event.response then local decodedResponse = json.decode(event.response) if decodedResponse["access\_token"] then OA2.accessToken = decodedResponse["access\_token"] debugWindow.addLine("Acc. Token:", OA2.accessToken) debugWindow.addLine() OA2.getCalendarList() end end end -- here we try to get a list of all the calendars available to the person who authenticated OA2.getCalendarList = function() local parameters = {} local headers = {} headers["Authorization"] = "Bearer " .. OA2.accessToken parameters.headers = headers network.request(OA2.getCalendarListURL, "GET", OA2.getCalendarListCallback, parameters) debugWindow.addLine("Get CalendarList request sent with access token") debugWindow.addLine() end -- and this will check the data we get from the previous get calendarlist request, and check and print the contents OA2.getCalendarListCallback = function(event) if event.response then local decodedResponse = json.decode(event.response) for k,v in pairs(decodedResponse.items) do debugWindow.addLine(k,v["id"]) end end end -- -- and here we start the code by opening up the webview and logging into Google! GO!!! -- OA2.openLoginWebView()

Looks like you might have left your real client ID/secret in the code.  I plan on trying it out later in the week.  Excited to see it in action.

Yup, I did leave my real ID and secret in there on purpose: it’s just a test app and there’s no real damage to be done, so feel free to use it (and save a little time and hassle). Either way, you’ll have to input your own gMail address and password so my data’s safe.

This is fantastic. I wonder if this is generic enough to use for any oAuth 2.0 services like Instagram? This might make a great pure Lua plugin if it’s generic enough, or a Google Only login plugin too.

Thanks Rob! The basic principle is generic enough to use with any Oauth2 services that allow rest, I think. The big challenge here was not the coding but digging through the crappy documentation. It’s safe to say that if it wasn’t for Google’s Oauth2 playground I would never have gotten this to work.

For those reading this:

  1. I’ve managed to get a lot of things working really well by now, getting and posting events to calendars.

  2. I won’t be posting or commenting, nor answering questions here in the next 3 weeks because I’m off on a vacation to a place without any practical form of internet - after that I’m happy to help.

  3. You should be good to using my code above, and the Google OAuth2 Playground.

Good luck!

Already a little info here:

The process for Oauth2 from Lua / Corona is as follows:

  1. You go to Google’s developer site to register your application and define which Oauth APIs you want to use. In return you get a client ID and client secret that you will need. You will also need to enter a “redirect URI” here. This was confusing to me at first, but as it turns out for Corona, it doens’t really matter what you enter here, as long as you use the same exact URI in your app.

  2. You open a webview window (so sort of a browser window) in your Corona app, and point it to a URL address that contains a lot of info in the URL, including your client ID and secret. The exact URL is defined by what service you want to access, and can be found in the Google Oauth2 playground easily.

  3. In this case, the URL goes to a Google form that is opened in the webview, and where you need to give permission to use your calendar data (by entering your google address and password, or if already logged in by simply clicking “allow”).

  4. When your login is correct, Google sends your browser to your “redirect URI”, and adds the Oauth2 authentication code in this process. You set an eventListener that detects when this page is loaded, and when “loaded” you can read out the URL and all the info that was embedded - in this case the important bit it is the Oauth2 authentication code.

  5. Once you have this authentication code, you can do a network request to another URL (all found in Google’s Oauth playground) where you add this auth. code in the request, and in return you get an access and refresh token in the response from Google!

  6. After that you have the most important bit: the access token! Once you have this, you can post requests to Google’s Calendar servers containing info about the specific data or operation you want, including the access tokens, and you well get a response from the server containing data (often in JSON format.).

Voila! That’s it. Most of the work is spelling out the POST request in the right way (using the right URL etc…), setting an eventListener to see when there is a response, and extracting the code you want out of the response.

Cool!

Sweet!  Thanks for posting, Thomas.

You’re welcome! I’m writing a more debugfriendly demo module as we speak, should be ready in an hour!

Here you go: this one’s a LOT better. Debugger integrated, and all the code is in logical order from top to bottom so you can read through and try to understand.

Run this on an iPad, preferably (or the simulator, as you need to read lots of fine text), and it should ask you to log in for your Google account, and in the end give you a listing of all the calendars you use (by email address).

------------------------------ -- main.lua ------------------ ------------------------------ display.setStatusBar( display.HiddenStatusBar ) local json = require( "json" ) local OA2 = {} local debugWindow = display.newGroup() debugWindow.lineCounter = 0 debugWindow.y = 2000 debugWindow.addLine = function(inputA, inputB) if inputA then local textA = display.newText(debugWindow, tostring(inputA), 0,debugWindow.lineCounter, native.systemFont, 32) textA.anchorX = 0 textA.x = 0 if inputB then -- second entry for this line local textB = display.newText(debugWindow, tostring(inputB), 200,debugWindow.lineCounter, native.systemFont, 32) textB.anchorX = 0 textB.x = 300 end end debugWindow.lineCounter = debugWindow.lineCounter + 40 debugWindow.y = debugWindow.y - 40 end -- Google Oauth2 data OA2.clientID = "996708749680-l7bntaouktpptiksm1mf0jp9m13suiji.apps.googleusercontent.com" OA2.clientSecret = "18wjQINlblj-xqHSBGWI4VxP" OA2.redirectURI = "https://www.google.com" OA2.authorizationURL = "https://accounts.google.com/o/oauth2/auth" OA2.scope = "https://www.googleapis.com/auth/calendar" OA2.accessTokenURL = "https://www.googleapis.com/oauth2/v3/token" OA2.getCalendarListURL = "https://www.googleapis.com/calendar/v3/users/me/calendarList" -- create webView OA2.openLoginWebView = function() OA2.formattedURL = string.format("%s?redirect\_uri=%s&response\_type=%s&client\_id=%s&scope=%s&approval\_prompt=%s&access\_type=%s", OA2.authorizationURL, OA2.redirectURI, "code", OA2.clientID, OA2.scope, "force", "offline") OA2.webView = native.newWebView( 768, 750, 1436, 1400) OA2.webView:request(OA2.formattedURL) OA2.webView:addEventListener( "urlRequest", OA2.authorizationListener ) end -- listen to what happens in the webview OA2.authorizationListener = function(event) for k,v in pairs(event) do debugWindow.addLine(k,v) end debugWindow.addLine() -- check to see if the authorization token was maybe handed to us in one of these webview events!! local checkForCode = string.match( event.url, 'code=(.+)' ) if checkForCode then OA2.authorizationCode = checkForCode debugWindow.addLine("GOT CODE!:", OA2.authorizationCode) OA2.webView:removeEventListener( "urlRequest", OA2.authorizationListener ) OA2.webView:removeSelf() OA2.requestTokens() end end -- once we have the authorizationCode we're going to send this code to Google to receive the OAuth2 access token and refresh token in return! OA2.requestTokens = function() local parameters = {} parameters.body = string.format("code=%s&redirect\_uri=%s&client\_id=%s&client\_secret=%s&scope=%s&grant\_type=%s", OA2.authorizationCode, OA2.redirectURI, OA2.clientID, OA2.clientSecret, OA2.scope, "authorization\_code") network.request(OA2.accessTokenURL, "POST", OA2.requestTokensCallback, parameters) debugWindow.addLine("Authorization code sent to Google in return for access and refresh tokens") end -- here we monitor the response we get to the token request, and see if there is an acces token inthere somewhere! OA2.requestTokensCallback = function(event) if event.response then local decodedResponse = json.decode(event.response) if decodedResponse["access\_token"] then OA2.accessToken = decodedResponse["access\_token"] debugWindow.addLine("Acc. Token:", OA2.accessToken) debugWindow.addLine() OA2.getCalendarList() end end end -- here we try to get a list of all the calendars available to the person who authenticated OA2.getCalendarList = function() local parameters = {} local headers = {} headers["Authorization"] = "Bearer " .. OA2.accessToken parameters.headers = headers network.request(OA2.getCalendarListURL, "GET", OA2.getCalendarListCallback, parameters) debugWindow.addLine("Get CalendarList request sent with access token") debugWindow.addLine() end -- and this will check the data we get from the previous get calendarlist request, and check and print the contents OA2.getCalendarListCallback = function(event) if event.response then local decodedResponse = json.decode(event.response) for k,v in pairs(decodedResponse.items) do debugWindow.addLine(k,v["id"]) end end end -- -- and here we start the code by opening up the webview and logging into Google! GO!!! -- OA2.openLoginWebView()

Looks like you might have left your real client ID/secret in the code.  I plan on trying it out later in the week.  Excited to see it in action.

Yup, I did leave my real ID and secret in there on purpose: it’s just a test app and there’s no real damage to be done, so feel free to use it (and save a little time and hassle). Either way, you’ll have to input your own gMail address and password so my data’s safe.

This is fantastic. I wonder if this is generic enough to use for any oAuth 2.0 services like Instagram? This might make a great pure Lua plugin if it’s generic enough, or a Google Only login plugin too.

Thanks Rob! The basic principle is generic enough to use with any Oauth2 services that allow rest, I think. The big challenge here was not the coding but digging through the crappy documentation. It’s safe to say that if it wasn’t for Google’s Oauth2 playground I would never have gotten this to work.

For those reading this:

  1. I’ve managed to get a lot of things working really well by now, getting and posting events to calendars.

  2. I won’t be posting or commenting, nor answering questions here in the next 3 weeks because I’m off on a vacation to a place without any practical form of internet - after that I’m happy to help.

  3. You should be good to using my code above, and the Google OAuth2 Playground.

Good luck!

Hi Thomas,
Is there any sample code?
Thanks!