OAuth access without GAS

Example getting OAuth access when the application is not behind GAS, for example a mobile app.

OAuthAPI helper functions

IMPORT com
IMPORT FGL OAuthAPI
IMPORT util

PUBLIC TYPE QueryUserInfoResponseBodyType DYNAMIC ARRAY OF RECORD
    user_id INTEGER,
    given_name STRING,
    family_name STRING,
    email STRING
END RECORD

# Error codes
PUBLIC CONSTANT C_SUCCESS = 0

PUBLIC FUNCTION QueryUserInfo(idp_url STRING, user_login STRING, user_password STRING) RETURNS(INTEGER, QueryUserInfoResponseBodyType)
    DEFINE fullpath base.StringBuffer
    DEFINE contentType STRING
    DEFINE req com.HttpRequest
    DEFINE resp com.HttpResponse
    DEFINE resp_body QueryUserInfoResponseBodyType
    DEFINE json_body STRING
    DEFINE expire INTEGER
    DEFINE meta OAuthAPI.OpenIDMetadataType
    DEFINE access_token  STRING
    DEFINE expires_in    INTEGER

    CALL OAuthAPI.FetchOpenIDMetadata(5, idp_url) RETURNING meta.*
    CALL OAuthAPI.RetrievePasswordToken(5, meta.token_endpoint, user_login, user_password, "profile") RETURNING access_token, expire 
    IF NOT OAuthAPI.InitService(5, access_token) THEN
      DISPLAY "Error: unable to initiate OAuthAPI"
      EXIT PROGRAM 1
    END IF

    TRY

        # Prepare request path
        LET fullpath = base.StringBuffer.create()
        CALL fullpath.append("/userinfo/v1/query")

        WHILE TRUE
            # Create oauth request and configure it
            LET req =
                OAuthAPI.CreateHTTPAuthorizationRequest(
                    SFMT("%1%2", meta.userinfo_endpoint, fullpath.toString()))

            # Perform request
            CALL req.setMethod("GET")
            CALL req.setHeader("Accept", "application/json")
            CALL req.doRequest()

            # Retrieve response
            LET resp = req.getResponse()
            # Retry if access token has expired
            IF NOT OAuthAPI.RetryHTTPRequest(resp) THEN
                EXIT WHILE
            END IF
        END WHILE
        # Process response
        INITIALIZE resp_body TO NULL
        LET contentType = resp.getHeader("Content-Type")
        CASE resp.getStatusCode()

            WHEN 200 #Success
                IF contentType MATCHES "*application/json*" THEN
                    # Parse JSON response
                    LET json_body = resp.getTextResponse()
                    CALL util.JSON.parse(json_body, resp_body)
                    RETURN C_SUCCESS, resp_body
                END IF
                RETURN -1, resp_body

            OTHERWISE
                RETURN resp.getStatusCode(), resp_body
        END CASE
    CATCH
        RETURN -1, resp_body
    END TRY
END FUNCTION