Web components / Examples |
This example shows how to authenticate the user with a google+ account on a mobile platform, using the OAuth technology.
The form file: wc_oauth.per
LAYOUT(text="Proceed to the authorization") GRID { [f1 ] [ ] [ ] } END END ATTRIBUTES WEBCOMPONENT f1 = FORMONLY.wc_oauth, STRETCH=BOTH; END
The Google+ API utility file: wc_oauth.4gl
# Google+ Authorization API: # See https://developers.google.com/accounts/docs/OAuth2InstalledApp IMPORT com IMPORT util # The persistant datastore PRIVATE DEFINE datastore RECORD client_id STRING, client_secret STRING, authorization_code STRING, expiration_date DATETIME YEAR TO SECOND, auth_data RECORD access_token STRING, token_type STRING, expires_in INTEGER, id_token STRING, refresh_token STRING END RECORD, user_info RECORD id STRING, name STRING, link STRING, # google plus profile URL picture STRING, # face URL email STRING END RECORD END RECORD #+ This function checks if the google account is authorized and manages to get authorization #+ @return boolean FUNCTION googleplus_isAuthorized() DEFINE httpReq com.HttpRequest DEFINE httpPostData STRING DEFINE httpResp com.HttpResponse DEFINE httpRespData STRING DEFINE authUrl STRING LET datastore.client_id = "****999.apps.googleusercontent.com" LET datastore.client_secret = "rlg*******-HUB" # Check token expiration IF datastore.expiration_date > CURRENT YEAR TO SECOND THEN RETURN TRUE END IF # The authorization token expired # If we already have an authorization, we need to refresh our token # See https://developers.google.com/accounts/docs/OAuth2InstalledApp?hl=fr#refresh IF datastore.auth_data.refresh_token.getLength() > 2 THEN # Refresh the token LET httpReq = com.HttpRequest.Create("https://accounts.google.com/o/oauth2/token") CALL httpReq.setMethod("POST") LET httpPostData = SFMT( "client_id=%1&client_secret=%2&refresh_token=%3&grant_type=refresh_token", datastore.client_id, datastore.client_secret, datastore.auth_data.refresh_token ) ELSE # Get an authorization code # See https://developers.google.com/accounts/docs/OAuth2InstalledApp?hl=fr#formingtheurl LET authUrl = SFMT( "https://accounts.google.com/o/oauth2/auth?" ||"response_type=code" ||"&client_id=%1" ||"&redirect_uri=urn:ietf:wg:oauth:2.0:oob" ||"&scope=https://www.googleapis.com/auth/userinfo.email" ||"%%20https://www.googleapis.com/auth/userinfo.profile" ||"%%20https://www.googleapis.com/auth/plus.login", datastore.client_id ) LET datastore.authorization_code = googleplus_getAuthorization(authUrl) IF datastore.authorization_code IS NULL THEN # User did not authorize the accesss to the data RETURN FALSE END IF # Ask for the first token # See https://developers.google.com/accounts/docs/OAuth2InstalledApp?hl=fr#handlingtheresponse LET httpReq = com.HttpRequest.Create("https://accounts.google.com/o/oauth2/token") CALL httpReq.setMethod("POST") LET httpPostData = SFMT("code=%1&client_id=%2&client_secret=%3" ||"&redirect_uri=urn:ietf:wg:oauth:2.0:oob" ||"&grant_type=authorization_code", datastore.authorization_code, datastore.client_id, datastore.client_secret ) END IF TRY CALL httpReq.doFormEncodedRequest(httpPostData, FALSE) LET httpResp = httpReq.getResponse() IF httpResp.getStatusCode() <> 200 THEN RETURN FALSE END IF LET httpRespData = httpResp.getTextResponse() CALL util.JSON.parse(httpRespData, datastore.auth_data) LET datastore.expiration_date = CURRENT YEAR TO SECOND + ((datastore.auth_data.expires_in - 60) UNITS SECOND) RETURN (datastore.expiration_date > CURRENT YEAR TO SECOND) CATCH # Network error... RETURN FALSE END TRY END FUNCTION #+ This function manages the authentication and authorization UI for Google+ #+ @param authorizationUrl the built URL to display the authorization dialog on google website #+ @return The authorization code FUNCTION googleplus_getAuthorization(authorizationUrl) DEFINE authorizationUrl STRING DEFINE authorizationCode STRING DEFINE wc_oauth STRING DEFINE doc_title STRING DEFINE flag BOOLEAN DEFINE authorizationCodeBegin INTEGER DEFINE authorizationCodeEnd INTEGER OPEN WINDOW w_oauth WITH FORM "wc_oauth" LET authorizationCode = NULL LET wc_oauth = authorizationUrl INPUT BY NAME wc_oauth ATTRIBUTES(WITHOUT DEFAULTS, ACCEPT=FALSE) ON CHANGE wc_oauth -- a new page is loaded in the webview CALL ui.Interface.frontCall("webcomponent","getTitle",["formonly.wc_oauth"],[doc_title]) IF doc_title.getIndexOf("Success", 1) == 1 THEN LET authorizationCodeBegin = doc_title.getIndexOf("code=", 1) LET authorizationCodeEnd = doc_title.getIndexOf("&", authorizationCodeBegin) IF authorizationCodeEnd = 0 THEN LET authorizationCodeEnd = doc_title.getLength() + 1 END IF LET authorizationCode = doc_title.subString(authorizationCodeBegin+5, authorizationCodeEnd - 1) EXIT INPUT END IF ON ACTION cancel MENU "Confirmation" ATTRIBUTES(STYLE="dialog", COMMENT="Cancel the authorization process?") ON ACTION accept LET flag = TRUE ON ACTION cancel LET flag = FALSE END MENU IF flag THEN LET authorizationCode = NULL EXIT INPUT END IF END INPUT CLOSE WINDOW w_oauth RETURN authorizationCode END FUNCTION
The program file: main.4gl
IMPORT FGL wc_oauth MAIN MENU ON ACTION get_auth IF googleplus_isAuthorized() THEN MESSAGE "Google+ authorization acquired." ELSE ERROR "Unable to get Google+ authorization." END IF ON ACTION close EXIT MENU END MENU END MAIN