This utility can generate and validate key pairs, generate SAML assertions accepted by SuccessFactors /oauth/token endpoint and integrate with Postman (which lacks support for the OAuth 2.0 SAML bearer assertion flow).
- Features
- Prerequisites
- Installation
- Usage
- Quick Start
- Generate a new key pair
- Create or update the OAuth client in SuccessFactors
- Run a web service returning OAuth access tokens
- Browser Form
- Usage with Postman
- Generate via CLI
- Argument Aliases
- Check the OAuth client certificate's validity
- Learning Only Users
- Testing
- Troubleshooting
- Contributing
- Sponsorship
- Generate RSA key pairs for SAML assertion signing
- Generate SAML bearer assertions accepted by the SuccessFactors OAuth token endpoint
- Run a local web service to request access tokens interactively from the browser
- Identify users by either
userIdorusername - Support learning-only users
- Reuse company-specific key pairs when available
- Keep a recent identifier history in the browser form for faster repeated use
- Integrate with Postman for OAuth 2.0 flows that Postman does not support natively
- NodeJS >= 18
- OpenSSL
$ npm i -g sf-oauth
⚠️ Once installed, you can run the commandsf-oauthfrom a terminal shell. Either pass the--dirargument to specify the directory (to be) containing the PEM files, or run from within that directory.
- Generate a key pair with
sf-oauth --newkeypair - Upload the generated public certificate to the SuccessFactors OAuth client configuration
- Start the local helper with
sf-oauth --dir <pem-directory> - Open the
/authorizeendpoint from your browser or call it directly from Postman
$ sf-oauth --newkeypair
...Provide sensible information for the certificate, for example:
Country Name (2 letter code) [AU]:BE
State or Province Name (full name) [Some-State]:Antwerp
Locality Name (eg, city) []:Antwerp
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example LTD
Organizational Unit Name (eg, section) []:HRT
Common Name (e.g. server FQDN or YOUR name) []:Pieter Janssens
Email Address []:piejanssens@example.com
- Go to OAuth Clients
- Create new or edit an existing client
- Provide a descriptive name - e.g. "Postman Pieter Janssens"
- Copy the contents of
...public.pem, paste in SF and save - Copy the OAuth client API key (e.g. to use as client ID in the Postman configuration)
Run the command without any arguments:
$ sf-oauth [--port]
ℹ️ PEM files directory is set to /X/Y/Z/SF Secret Keypairs
ℹ️ Check the README.md for instructions on how this can be used in combination with Postman
🚀 SAML Assertion OAuth access token generator listening on port 3000| method | path | purpose | body/query parameters |
|---|---|---|---|
| GET | /authorize |
requests to supply identifier via your browser, generates SAML assertion, requests OAuth access token from SF and then returns the access token | client_id, scope, state, learning_only (optional) |
| POST | /authorize |
immediately generates SAML assertion, requests OAuth access token from SF and then returns the access token | user_id or username, client_id, scope, state, learning_only (optional) |
⚠️ If a specific keypair with the name '-public.pem' and '-private.pem' is present, this will be used to generate the SAML assertion. If not, by default it will use 'public.pem' and 'private.pem'.
When you open GET /authorize without a user_id, the app renders a browser form that helps you complete the token request.
The form supports:
- Choosing between
User IDandUsername - Entering the identifier manually or selecting one from recent history
- Marking the request as
Learning Only User - Reusing up to 20 recent identifiers stored in the browser's local storage
The recent identifiers are shown in a list on the right side of the page and can be clicked to populate the identifier input.
For each SF instance, create a separate Postman environment specifying the following variables:
- hostname: hostname of SF API
- company_id: SF instance ID
- client_id: SuccessFactors OAuth client API key
In any collection or folder, set up 'Authorization' to OAuth 2.0 and configure the like shown as follows:
Remember to select the correct Postman environment prior to requesting a new OAuth access token. As long as the token remains valid you can select different tokens that are held by Postman without the need to generate/request a new one.
$ sf-oauth --generate --companyId <SF Company ID> --hostname <SF API hostname> --clientId <OAuth client API key> (--userId <userId> | --username <username>) [--ttl <assertion validity in seconds>] [--learningOnly]
SAML Assertion...
base64 encoded SAML AssertionOptional parameters:
--dir: directory containing the PEM files. If omitted, the current working directory is used.--ttl: validity of the assertion in seconds (600 by default)--validate: will request a bearer access token and validate it on by calling the SF OData API, this requires the argument--companyIdto be provided as well.--raw: will output the base64 encoded string only. This can be used in scripting or piping. For example 🪄$ sf-oauth --generate --companyId ... --raw | base64 -d
For user identification, provide exactly one of these:
--userId: use the SuccessFactors user ID--username: use the SuccessFactors username
Example of generating a SAML assertion, requisting an access token with it and finally testing the access token by calling the SuccessFactors OData API:
$ sf-oauth --generate --companyId salesDemoXYZ --hostname apisalesdemo2.successfactors.eu --clientId NzNkYzk0NTljMTQ0NWEyOWMxNzUwYjdhOTdkOA --username piejanssens@example.com --ttl 3600 --validate
Requesting a SAML Bearer token...
Bearer token received 🎉
{
access_token: 'eyJ0b2tlbkNvbnRlbnQiOnsiYXBpS2V5IjoiTnpOa1l6azBOVGxqTVRRME5XRXlPV014TnpVd1lqZGhPVGRrT0EiLCJzZlByaW5jaXBsZSI6IjEwMzI2NiNESVYjU0ZDUEFSVDAwMDUxMiIsImlzc3VlZEZvciI6InBqX25vZGVqcyIsInNjb3BlIjoiIiwiaXNzdWVkQXQiOjE2NDc1MTI0NDU4OTIsImV4cGlyZXNBdCI6MTY0NzU5ODg0NTg5Mn0sInNpZ25hdHVyZSI6IklQSTEvbGh3dGtIeXFQTml0bzNIL05DL3hzSjFSMHBYM3hMOCt0RWlFN29OYnhveFVOc1lUOUlyMnorZlUxN0JEcFc2eWhHU1dPaERHRjJjUTQ3dVZGNHJGLzd2cXRPTlZGbWdvK2NGTDBNSUsxS1Axck1BK29DM0paU1ZOL2RTaWFzWXJUb1BrdnBkZ3BGcHN0U2VYc3lvajFxWTdVL1daSllhbDZzakd4WT0ifQ==',
token_type: 'Bearer',
expires_in: 85949
}
Validating the token...
Token is valid 🎉| alias | argument |
|---|---|
| -g | --generate |
| -n | --newkeypair |
| -c | --clientId |
| -u | --userId |
| -U | --username |
| -i | --companyId |
| -h | --hostname |
| -v | --validate |
| -t | --ttl |
| -p | --port |
| -r | --raw |
| -d | --dir |
| -l | --learningOnly |
$ sf-oauth --validate [--companyId]
notAfter=Mar 6 13:37:03 2032 GMTThe SuccessFactors Learning OAuth token server is deprecated. Instead, you can use the SuccessFactors Platform token server to generate OAuth tokens even if the user does not exist in Employee Profile or Employee Central, a so-called learning-only user.
Via CLI: Use the -l or --learningOnly argument.
Via Web Form: When using the /authorize endpoint, check the "Learning Only User" checkbox.
Example:
$ sf-oauth --generate --companyId salesDemoXYZ --hostname apisalesdemo2.successfactors.eu --clientId <key> --userId <user> --learningOnlyRun the automated tests locally with:
npm testThe test suite uses dummy key pairs stored in test/fixtures/*.pem for assertion generation tests.
GitHub Actions runs the same test command on every pull request and on pushes to master.
Make sure you either run sf-oauth from the directory containing the PEM files or pass --dir <pem-directory> explicitly.
Expected files are:
public.pemandprivate.pem, or<companyId>-public.pemand<companyId>-private.pem
Check these items first:
- The OAuth client in SuccessFactors contains the correct public certificate
- The
client_idmatches the OAuth client API key from SuccessFactors - The hostname points to the correct SuccessFactors API domain
- The certificate is still valid and not expired
You can inspect certificate validity with:
sf-oauth --validate [--companyId <SF Company ID>]This usually means the wrong Postman environment is selected or one of the required variables is missing.
Verify that your active environment defines:
hostnamecompany_idclient_id
Choose exactly one identifier type:
--userIdfor a SuccessFactors user ID--usernamefor a SuccessFactors username
In the browser form, select the matching radio option before submitting.
If the user does not exist in Employee Profile or Employee Central, enable learning-only mode:
- In the CLI, add
--learningOnly - In the browser form, check
Learning Only User
Start the local server on a different port:
sf-oauth --port 3001Contributions are more than welcome! Please open an issue or a pull request.
ℹ️ To be able to execture the Node cli commands on your forked source code, run npm link from the root folder project.

