| 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