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