Generating PS_TOKENs Natively in PeopleCode
The ability to generate PS_TOKENs opens up a useful capability in PeopleSoft: impersonating users for troubleshooting and testing. A valid PS_TOKEN can be set as a browser cookie or passed directly into the delviered SwitchUser function, and the session will switch to whichever user the token represents.
The problem is that PeopleSoft doesn’t give you an API to generate a PS_TOKEN for another user. You can only generate a token for the user you are currently authenticated as. Security research years ago revealed the PS_TOKEN format, making manual generation possible as long as you also had the node password. Without that password, you can’t build the hash portion of the token, and the system will reject it.
Recently, while digging through delivered PeopleCode, I realized PeopleSoft exposes a way to retrieve node passwords in clear text. When you combine this with a PeopleCode routine that constructs a PS_TOKEN byte-for-byte, you can generate valid tokens for arbitrary users with nothing more than a running authenticated session.
This post goes through the two key pieces: retrieving the node password, and assembling the token structure in PeopleCode.
Obtaining Node Passwords in PeopleCode
There may be other ways to derive a node’s clear-text password in PeopleCode, but a simple and direct approach is to use the PT_IB_NODE:NodeDefinition class. This class has a getter property called IbPassword which returns the decrypted password for the node you pass in.
For example, retrieving the password for a node named PSFT_CS is as simple as:
Local string &sNodePassword = (create PT_IB_NODE:NodeDefinition("PSFT_CS")).IbPassword;
As mentioned earlier, this password is the one required piece you must have in order to generate PS_TOKENs that PeopleSoft will accept.
Generating PS_TOKENs with a Known Node Password
The node password is what allows you to compute the SHA-1 signature block embedded inside PS_TOKENs. The rest of the token is mostly straightforward: User ID, language code, timestamp, and node name, along with a handful of static header bytes PeopleSoft uses.
All of these values are concatenated in a specific structure, compressed, hashed, and then base64 encoded. If you know the target user’s ID and the node password, you have everything required to generate a valid PS_TOKEN. If the token is assembled correctly, PeopleSoft will accept the ID inside it as the authenticated user without requiring that user’s password.
Below is a PeopleCode App Class implementation that builds valid PS_TOKEN values for any user.
Click to expand code
How to install
-
In App Designer, create (or choose) an App Package where you want this code to live.
-
Inside that package, create a new Class named
PSTokenUtil. -
Copy/paste the above code (direct link) into the class definition and save.
PSTokenUtil Details and Usage
Note
The code examples in this section assume the PSTokenUtil class resides in a package named PSM_AUTH.
The PSTokenUtil constructor takes a single Boolean that controls what endianness to use when building PS_TOKEN values. Passing True uses little endian; passing False uses big endian.
For the majority of PeopleSoft environments, little endian is the correct choice and should always be used. If you find that the tokens generated by this class are not accepted by your system, try switching to big endian and test again.
Here is how to instantiate the class using little endian:
Local PSM_AUTH:PSTokenUtil &oTokenUtil = Create PSM_AUTH:PSTokenUtil(True);
The main method that performs the token construction is GenTokenForValues. This method takes the key pieces needed to construct a PS_TOKEN: User ID, language code, issue timestamp, and node name. It then retrieves the node password for the supplied node (via PT_IB_NODE:NodeDefinition), constructs the token according to the selected endianness, and returns the final PS_TOKEN as a string.
Here is an example of generating a token for the PS user using the PSFT_CS node:
Local PSM_AUTH:PSTokenUtil &oTokenUtil = create PSM_AUTH:PSTokenUtil(True);
Local string &sPsToken = &oTokenUtil.GenTokenForValues("PS", "ENG", %Datetime, "PSFT_CS");
The returned token can be placed directly into the browser’s PS_TOKEN cookie or passed to the SwitchUser function to change the session context to the PS user.
The class also provides two convenience methods, GenToken and SwitchUser, which build tokens using the current session’s language code, issue timestamp, and node information.
-
GenToken(OPRID)returns a PS_TOKEN string for the specified user. -
SwitchUser(OPRID)generates the token internally, then calls the delivered PeopleCodeSwitchUserfunction to change the session context to the specified user.
Example: GenToken
Local PSM_AUTH:PSTokenUtil &oTokenUtil = create PSM_AUTH:PSTokenUtil(True);
Local string &sPsToken = &oTokenUtil.GenToken("PS");
Example: SwitchUser
Local PSM_AUTH:PSTokenUtil &oTokenUtil = create PSM_AUTH:PSTokenUtil(True);
Local boolean &bRet = &oTokenUtil.SwitchUser("PS");
If Not &bRet Then
throw CreateException(0, 0, "Failed to switch user");
End-If;
Additional Notes and Clarifications
-
This utility is only capable of generating PS_TOKENs for node definitions that are configured to use password authentication. It will not work for nodes that use digital certificate authentication.
-
This utility may not work for generating PS_TOKENs to be used in single sign on (SSO). For example, if you have HR and CS systems participating in SSO, a token generated and signed for the CS node may not successfully authenticate a user in the HR system.
-
For switching users with this utility, the environment must have Switch User functionality enabled (PeopleTools > Utilities > Administration > PeopleTools Options). The
Enable Switch Useroption must be set toAllorSome. If it is set toSome, you must also check theAllow Switch Userbox on the User Profile (PeopleTools > Security > User Profiles > User Profiles) for any users you want to switch between. -
I have not yet been able to test this utility against a big endian PS_TOKEN, so the big endian branch of the logic may be incorrect. All of my testing so far has been with little endian tokens.
-
This utility is a PeopleCode adaptation of the logic found in the peoplesoft-token-extractor Burp plugin, which was derived from Alexey Tyurin’s (ERPScan) TokenChpoken Python script.