Bullhorn Identity is a new solution for our customers to enable a more seamless experience between the Bullhorn ATS/CRM and other applications, including partner offerings. This will enable users to have a single set of credentials to gain access to multiple applications, using a new feature called ‘Login with Bullhorn’.
Basic OAuth Flow
Step 1: Partner makes an /oauth/authorize request
Example https://universal.bullhornstaffing.com/universal-login/oauth/authorize?response_type=code&client_id=96a1deec-8d6c-43f6-8018-0ce1d1371fb0&redirect_uri=https://example-app.com/cb
required arguments
-
response_type=code
-
client_id : partners specific client_id
-
redirect_uri : redirect URL for the partner site to handle OAuth 2.0 responses
Step 2: Universal Loginvalidates the client_id and redirect_uri
Step 3: Universal Loginrequests user’s credentials with Bullhorn ATS/CRM Login Page
Step 4: Universal Loginvalidates the user’s provided credentials
Step 5: Universal Loginredirects to the requested redirect_uri with an authorization_code
Example:
https://example-app.com/cb?code=Yzk5ZDczMzRlNDEwY&state=5ca75bd30
Step 6: Partner responds with /oauth/token call
Example:
POST /oauth/token HTTP/1.1
Host: universal.bullhornstaffing.com/universal-login
code=Yzk5ZDczMzRlNDEwY&state=5ca75bd30
&grant_type=authorization_code
&redirect_uri=https://example-app.com/cb
&client_id=96a1deec-8d6c-43f6-8018-0ce1d1371fb0
&client_secret=LP2kplxZcz84VNn1
required arguments
- grant_type =code
- code : from /oauth/authorize redirect
- client_id : same as used in /oauth/authorize call
- client_secret : partner app specific client_secret
- redirect_uri : same as used in /oauth/authorize call
Step 7: Universal Loginvalidates the client_id and redirect_uri match the authorization_code
Step 8: Universal Loginvalidate the client_secret matches the client_id
Step 9: Universal LoginDetermine the “time to live” for a user’s session based on their private label
Step 10: Returns HTTPS Only cookie to the browser and the following response:
Example
{
"refresh_token": "44_5183932_1:d0f94a92-6b06-45c5-8171-c47b6b71e387",
"token_type": "Bearer",
"expires_in": 900,
"restUrl": "http://example-backend.bh-bos2.bullhorn.com:8182/rest-services/1hs/"
}
- token_type : Always bearer
- expires_in : expiration of the access_token
- restUrl: user-specific REST URL that should be used for all follow up OAuth calls for the user.
Handle user linking
After the user gives consent to access their Bullhorn CRM profile, Bullhorn sends a request that contains a signed assertion of the Bullhorn CRM user’s identity. The assertion contains information that includes the user’s Bullhorn CRM unique mastgerUserId and username. You should then check whether a user with the unique Bullhorn CRM masterUserId already exists in your system.
When decoded, the access_token (JWT) is formatted like the following example:
Example
{
"@class": "com.bullhorn.common.session.UserAccessTokenSessionImpl",
"corpId": 44,
"masterUserId": 5183932,
"userId": 5119506,
"clientId": "96a1deec-8d6c-43f6-8018-0ce1d1371fb0",
"features": [],
"privateLabelId": 4763,
"userTypeId": 38085,
"username": "x",
"accessTokenKey": "44_5183932_1:ec7fd3f8-b1cb-4d31-ab89-911e8b21b800",
"jti": "9779668c-3273-4d54-b907-a9388b26f956",
"iss": "example-backend",
"sub": "USER-5183932",
"aud": "96a1deec-8d6c-43f6-8018-0ce1d1371fb0",
"iat": 1646082140,
"nbf": 1646082020,
"exp": 1646083040
}
The following fields from the JWT should be used for account linking:
JWT fields sub - Bullhorn CRM unique masterUserId with they user type (corp,user) prepended username - Users unique Bullhorn CRM username
The first time a user follows the ‘Login with Bullhorn’ OAuth flow, after Step 6 in the example above, the partner application should store the sub field on the associated user in the partner’s user database. This can then be used to match against future access_tokens.
Already logged in users
If the user already has an active session, and a corresponding Novo cookie or a secure JWT cookie from Bullhorn Identity, Bullhorn will detect the existing cookie and use it for authentication. This will bypass the user prompt for login and instead validate the existing cookie and proceed to ‘Step 5: Universal Loginredirects to the requested redirect_uri with an authorization_code’
Additional supported endpoints /oauth/logout /oauth/refresh /oauth/userinfo
Recommendations
Handling Expired access_token
When a user has an expired access_token, it is always preferable to first check if there is a refresh token. If one exists, the partner should make the /oauth/token call and pass the refresh_token instead of a new /oauth/authorize call.
Handling partner application logouts
If a user implicitly logs out of the partner applications that has used ‘Login with Bullhorn’ for authentication, a /oauth/logout call should be made in order to properly expire the related Bullhorn CRM session
Note: this currently only logs out the sessions associated with the access_token. All other active Bullhorn CRM sessions related to the same user will remain active