Implement OAuth in a Non-Interactive Integration

A Non-Interactive Integration is one that calls APIs without any user interaction. Typical examples would be scripted integrations between a 3rd party application and Oracle Primavera Cloud, or software that runs on a scheduled basis and calls APIs to access data without any user interface.

A Non-Interactive Integration is a trusted client that uses the JWT Bearer (otherwise known as User Assertion) grant type to request an access token, as described below.

When requesting an access token, a user assertion is provided to request authorization on behalf of a user within your organization.

There is no refresh flow for Non-Interactive Integrations. When the access token expires, your integration should request a new access token.

Step 1: Obtain Client Credentials

Your Non-Interactive Integration will be registered as a trusted client and you will receive client credentials in the form of a Client ID and Client Secret.

These credentials must be kept confidential and must not be shared with anyone outside of your organization.

Step 2: Create a User Assertion

An assertion is a package of information that facilitates the sharing of identity and security information across security domains. A user assertion contains information about a user together with information about the conditions under which the assertion is to be considered valid.

By providing a user assertion in the request for an access token, a non-interactive integration can receive an access token allowing access to APIs on behalf of the identified user.

Lobby supports user assertions in JSON Web Token (JWT) format that are signed by the private key of a trusted issuer. Lobby uses the corresponding public certificate of the trusted issuer that was specified in the registration of your integration to validate the supplied user assertion.

More information about JWT can be found on https://jwt.io/introduction.

A user assertion comprises a header, body and signature.

The header has the following attributes:

Name Value
kid Key identifier. Used to identify the trusted third-party certificate to validate the assertion signature.  Must match the certificateAlias of the uploaded public certificate.
type Type. Identifies the type of assertion, which is always "JWT".
alg Algorithm. Identifies the specific type of JWT signing algorithm, which is always "RS256".
x5t Base64 URL encoded X.509 certificate sha1 thumbprint. Used to identify the trusted third-party certificate to validate the assertion signature. The x5t or kid claim must be present in the JWT assertion header.

The following claims must be included in the body:

Name Value
sub Subject. The Primavera Cloud account username.
iss Issuer. The Client ID of your registered integration.
aud Audience. Identifies the recipients for which the JWT is intended, which is always "https://identity.oraclecloud.com".
exp Expiration. The time (UNIX epoch time) when the JWT assertion expires.
iat Issued at. The date (UNIX epoch time) when the assertion was issued.
jti JWT Identifier.  Unique identifier for the JWT. A JWT ID can only be used once.

Here is an example JSON header and body:

{"kid":"MyCertificateAlias","type":"JWT","alg":"RS256"}
{"sub":"patrick.oleary@majestic.com","iss":"MyClientID","aud":"https://identity.oraclecloud.com/","exp":1708778535,"iat":1708774935,"jti":12345}

The header and body are Base64-encoded and concatenated by a dot (.) then signed in the RS256 algorithm, using the trusted issuer's private key.

The result is three Base64 strings separated by dots (.) in the format of Header.Body.Signature

Here is an example signed user assertion:

Here is an example JSON header and body:

eyJraWQiOiJNeUNlcnRpZmljYXRlQWxpYXMiLCJ0eXBlIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJwYXRyaWNrLm9sZWFyeUBtYWplc3RpYy5jb20iLCJpc3MiOiJNeUNsaWVudElEIiwiYXVkIjoiaHR0cHM6Ly9pZGVudGl0eS5vcmFjbGVjbG91ZC5jb20vIiwiZXhwIjoxNzA4Nzc4NTM1LCJpYXQiOjE3MDg3NzQ5MzUsImp0aSI6MTIzNDV9.jaQ2NyGk8wOWWHMGi2QJTsYlKGcHrfqkvP2Gb8AlBbJDQy7NDonXh6YMcAe17iIVaOfH7lDgJyF95xPv3nPHdIezbqobHBck34yct6I6a_xpKcV5kmJfXLHeb9LenqZTbdMMQ95vlUL8R914AmE2TbwGqjl4XkIoADHDez7PVM2MwyIDSfEaQ6o7J05ES7wIgI9gGspQ5w-2Xem4GOare25FBo-LrgVADDiAhKUhSLNT6XISCMAHZ3L2J86cnRhU1fekr-DJYFyfDcgAZeQPSPETHGokBWYtC1K-2qIouODKBKBcooABYEh6YTkC7bdax5KqFFbvJmSfDEjyN3tZ4w

Oracle Security Developer Tools (OSDT) can be used to generate a signed JWT User Assertion using the  Oracle JSON Web Token class. Alternatively, third party libraries can be on https://jwt.io/libraries.

The Oracle Java Key and Certificate Management Tool can be used to generate a signing key pair.

Step 3: Exchange the signed JWT for an Access Token

Your application exchanges the signed JWT for an Access Token by making a POST request to the token endpoint of the Lobby specifying the client credentials using Basic Authentication ( Base64 encoding of the Client ID and Secret joined by a colon ':'), as follows:

POST https://<LOBBY>/auth/token
 
Headers:
  Content-Type: application/x-www-form-urlencoded
  Authorization: Basic <BASE_64_ENCODED_CLIENT_CREDENTIALS>
 
Body:
  grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
  assertion=<SIGNED_JWT>

Here is an example curl request:

curl --request POST 'https://constructionandengineering.oraclecloud.com/auth/token' \
--header 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8' \
--header 'Authorization: Basic TXlDbGllbnRJRDpNeUNsaWVudFNlY3JldA==' \
--data 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer ' \
--data 'assertion=TXlBdXRob3JpemF0aW9uQ29kZQ..tyFRW'

The response is in JSON format and contains the access token, expiration time of the access token and refresh token.

Here is an example response:

{
    "access-token": "eyJ4NXQjUzI1NiI...KtK5elB38rcAbgFtVP9A",
    "token-type": "Bearer",
    "expires-in": 7200,
    "refresh_token": "TXlSZWZyZXNoVG9rZW4="
}

Note: If you are unable to generate a User Assertion that contains the user_id and user_site claims, and there are multiple Primavera Cloud accounts linked to the Lobby account specified by the sub claim, you can specify these claims in the request body. You cannot specify these claims in both the User Assertion and the request body.

Step 4: Use the Access Token

Use the Access Token to make authorized requests to APIs on behalf of the authenticated user by including it in the Authorization header of your HTTP requests, as follows:

Authorization: Bearer <ACCESS_TOKEN>

Here is an example curl request:

curl 'https://primavera-eu1.oraclecloud.com/api/restapi/project/{projectId}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJ4NXQjUzI1NiI...KtK5elB38rcAbgFtVP9A'

Step 5: Refresh the Access Token

When the Access Token expires, make the same POST request to the token endpoint of the Lobby to get a new Access Token. There is no refresh token flow for Non-Interactive Integrations.