In this section, we will modify the PowerBuilder application source code and the PowerServer project settings to achieve the following results:
-
Sends the user credentials and/or password to the JWT server and gets a token from the JWT server if authentication is successful.
-
Uses the token to access data from the PowerServer Web API.
-
Refreshes the token when necessary.
Step 1: Declare the following global variables.
//Token expiresin Long gl_Expiresin //Refresh token clockskew Long gl_ClockSkew = 3
Step 2: Define a global function and name it f_Authorization().
Select from menu File > New; in the New dialog, select the PB Object tab and then select Function and click OK to add a global function.
This global function uses the HTTP Post method to send the user credentials to the authentication server and then gets the token from the HTTP Authorization header.
Add scripts to the f_Authorization() function to implement the following scenario: When the application starts, the application uses the username and password from the login window to get the token, and when the token expires, the login window displays for the user to input the username and password again.
Note: The following scripts use the username and password from the INI file instead of from the login window. You can change the scripts to use the login window after you implement the login window and return the username and password to the f_Authorization() function.
//Integer f_Authorization() for password //UserName & Password are passed from the login window RestClient lrc_Client String ls_url, ls_UserName, ls_UserPass, ls_PostData, ls_Response, ls_expires_in String ls_TokenType, ls_AccessToken String ls_type, ls_description, ls_uri, ls_state Integer li_Return, li_rtn JsonParser ljson_Parser li_rtn = -1 ls_url = profilestring("CloudSetting.ini","setup","TokenURL","") //login window can be implemented to return username & password according to actual needs. //Open(w_login) //Return UserName & Password ls_UserName = ProfileString("CloudSetting.ini", "users", "userName", "") ls_UserPass = ProfileString("CloudSetting.ini", "users", "userPass", "") If IsNull ( ls_UserName ) Or Len ( ls_UserName ) = 0 Then MessageBox( "Tips", "UserName is empty!" ) Return li_rtn End If If IsNull ( ls_UserPass ) Or Len ( ls_UserPass ) = 0 Then MessageBox( "Tips", "Password is empty!" ) Return li_rtn End If ls_PostData = '{"Username":"' + ls_UserName + '", "Password":"' + ls_UserPass + '"}' lrc_Client = Create RestClient lrc_Client.SetRequestHeader("Content-Type","application/json") li_Return = lrc_Client.GetJWTToken( ls_Url, ls_PostData, ls_Response ) If li_Return = 1 and Pos ( ls_Response, "access_token" ) > 0 Then ljson_Parser = Create JsonParser ljson_Parser.LoadString(ls_Response) ls_TokenType = ljson_Parser.GetItemString("/token_type") ls_AccessToken = ljson_Parser.GetItemString("/access_token") //Application Set Authorization Header Getapplication().SetHttpRequesTheader("Authorization", ls_TokenType + " " +ls_AccessToken, true) //Set Global Variables gl_Expiresin = Long (ljson_Parser.GetItemNumber("/expires_in")) li_rtn = 1 Else MessageBox( "AccessToken Falied", "Return :" + String ( li_Return ) ) End If If IsValid ( ljson_Parser ) Then DesTroy ( ljson_Parser ) If IsValid ( lrc_Client ) Then DesTroy ( lrc_Client ) Return li_rtn
Step 3: Insert a timing object (timing_1) to the application and add the following scripts to the Timer event of timing_1.
1) Open the application object and then select from menu Insert > Object > Timing to add a timing object to the application.
2) Add the following scripts to the Timer event of timing_1.
//Authorization f_Authorization()
When displayed in the source editor, the Timer event looks like this:
event timer;//Authorization f_Authorization() end event
Step 4: Add the following scripts to the application Open event.
Place the scripts before the database connection is established. The scripts get the token from the JWT server and then start the user session (using the BeginSession function) to include the token information in the session.
//Authorization If f_Authorization() <> 1 Then Return End If //StartSession long ll_return Try ll_return = Beginsession() If ll_return <> 0 Then Messagebox("Beginsession Failed:" + String(ll_return), GetHttpResponseStatusText()) End if Catch ( Throwable ex) MessageBox( "Throwable", ex.GetMessage()) Return End Try //Refresh Token for timing If gl_Expiresin > 0 And (gl_Expiresin - gl_ClockSkew) > 0 Then //Timer = Expiresin - ClockSkew //7200 - 3 timing_1.Start(gl_Expiresin - gl_ClockSkew) End If //Connect db
Step 5: Add the following scripts to the SystemError event.
The scripts will trigger the SystemError event when the session or license encounters an error; and if the token is invalid or expires, the scripts will call the f_Authorization function to get the token again.
Choose Case error.Number Case 220 to 229 //Session Error MessageBox ("Session Error", "Number:" + String(error.Number) + "~r~nText:" + error.Text ) Case 230 to 239 //License Error MessageBox ("License Error", "Number:" + String(error.Number) + "~r~nText:" + error.Text ) Case 240 to 249 //Token Error MessageBox ("Token Error", "Number:" + String(error.Number) + "~r~nText:" + error.Text ) //Authorization f_Authorization() Case Else MessageBox ("SystemError", "Number:" + String(error.Number) + "~r~nText:" + error.Text ) End Choose
Create an INI file in the same location as the PBT file and name it CloudSetting.ini.
Specify the URL for requesting the token from the JWT server in the CloudSetting.ini file. Notice that TokenURL points to the "/connect/token" API of the built-in JWT server, and the JWT server root URL (for example, http://localhost:5099/) is the same as the root URL of PowerServer Web APIs. If you change the PowerServer Web API root URL, change the root URL here accordingly.
[Setup] TokenURL=http://localhost:5099/connect/token
To get the username and password from the INI file (instead of from the login window), you need to add the following section to the CloudSetting.ini file and set the user name and password accordingly.
[users] userName=alice userPass=alice
By default, the user session is automatically created when the application starts; and the session includes no token. For the session to include the token, the session must be started manually by code instead of automatically.
To start the session manually by code,
Step 1: Enable "Begin session by code" in the PowerBuilder IDE. (Steps: Open the application object painter, click Additional Properties in the application's Properties dialog; in the Application dialog, select the PowerServer tab and then select the Begin session by code option and click Apply.)
After this option is enabled, when the BeginSession function in the application Open event is called, it will create a session that includes the token information (See scripts in step 4 in "Add scripts").
Step 1: Add the INI file CloudSetting.ini to the Files preloaded in uncompressed format section in the Client App page.
Step 2: Select RESTClient Support under the Runtime files group.
Step 3: Double check the URL of the PowerServer Web APIs in the .NET Server page.
Make sure the port number is not occupied by any other program. You can execute the command "netstat -ano | findstr portnumber" to check if the port number is occupied by any other program. For details, refer to Choosing an appropriate port number.
The built-in JWT server will run at the same URL as the PowerServer Web API. If the PowerServer Web API URL is changed, change the JWT server root URL accordingly in the INI file.
Step 4: Double check that Use built-in JWT server is selected from the Auth Template list box in the .NET Server page > Advanced tab.
Step 5: Save the changes and deploy the PowerServer project (using the "Build & Deploy PowerServer Project" option) so that the above settings can take effect in the installable cloud app.