Generating the JWT User Assertion

You must generate a signed, encoded JWT using the private key which links to the public key / signing certificate uploaded into the Confidential Application. Requirements for the JWT header and payload are outlined in the Generate the JWT user assertion section of the Oracle Cloud Platform REST Adapter documentation at Authentication Support, Use OAuth 2.0 Grants in Identity Domain Environments, Prerequisites for JWT User Assertion.

A user assertion includes a header, body and signature.

The header must include 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. Either the x5t or kid claim must be present in the JWT assertion header.

x5t

 

Base64 URL encoded X.509 certificate sha1 thumbprint. Used to identify the trusted third-party certificate to validate the assertion signature. Either the x5t or kid claim must be present in the JWT assertion header.
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".

The body must include the following claims:

Name Value
sub Subject. The Primavera Cloud account username.
iss Issuer. The Client ID of your confidential application.
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":"P6WS_UserName","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 your 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:

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

There are several libraries available to generate/sign the JWT, here: https://jwt.io/libraries

Example using Python

import requests, base64, uuid, sys, hashlib 
      from datetime import datetime, timezone ##timezone only available in python 3.2+ 
      import jwt ##jwt requires "pip install pyjwt" 
      from OpenSSL.crypto import load_pkcs12, dump_privatekey, dump_certificate, FILETYPE_PEM, FILETYPE_ASN1 
       
      #script can be executed as follows: 
      #python3 getOAuthToken_example.py <username> <expiry> 
      #Ex: python3 getOAuthToken_example.py JonesE 86400 
      #    where 'JonesE' is the username and 86400 is the expiry in seconds 
       
      #This python script example includes both the JWT user assertion generation and 
      #OAuth access token generation using base64-encoded client ID and secret 
       
      #MISC VARIABLE CREATION 
      userName = sys.argv[1] 
      expiryDuration = int(sys.argv[2]) 
      tokenIssued = int(datetime.now(tz=timezone.utc).timestamp()) 
      tokenExpiry = tokenIssued + expiryDuration 
      tokenJti = str(uuid.uuid4()) #generates random UUID 
      tokenEndpoint = '/oauth2/v1/token' 
      idcs_url = 'https://<idcs_tenant_url>' + tokenEndpoint 
      clientId =  '<clientid_from_confidential_application>' 
      clientSecret = '<clientsecret_from_confidential_application>' 
      audience = 'https://identity.oraclecloud.com/' 
      signing_alg = 'RS256' 
       
      #SCOPE VARIABLE CREATION 
      #scope = 'urn:opc:idm:__myscopes__'  #allows an expiry range between 60s to 3600s 
      scope = 'urn:opc:idm:__myscopes__ urn:opc:resource:expiry=' + str(tokenExpiry)  #allows an expiry range between 60s to 31556952s 
      #LOAD PKCS12 AND READ PRIVATE KEY IN PEM FORMAT 
      p12_file = open('/home/oracle/jwt.p12', 'rb').read()  #path is relative to location of P12 file 
      p12_pwd_bytes = "password1".encode('utf8')  #password is relative to p12 file 
      p12 = load_pkcs12(p12_file, p12_pwd_bytes) 
      private_key = dump_privatekey(FILETYPE_PEM, p12.get_privatekey()) 
       
      #GENERATE KID FOR JWT USER ASSERTION 
      #Equal to the public certificate alias uploaded to your IDCS confidential application 
      certAlias = 'jwtkey'  #alias is relative to the alias of private key 
       
      #GENERATE X5T FOR JWT USER ASSERTION 
      #Equal to the base64, url-encoded X.509 certificate sha1 thumbprint 
      cert = p12.get_certificate() 
      cert_der = dump_certificate(FILETYPE_ASN1, cert) 
      sha1_hash = hashlib.sha1(cert_der).digest() 
      x5t = base64.urlsafe_b64encode(sha1_hash).decode('utf8').rstrip('=') 
       
      #BASE64 ENCODE CLIENTID:CLIENTSECRET 
      clientIdSecret = clientId + ':' + clientSecret 
      clientIdSecret_bytes = clientIdSecret.encode("ascii") 
      clientIdSecret_base64_bytes = base64.b64encode(clientIdSecret_bytes) 
      clientIdSecret_base64_string = clientIdSecret_base64_bytes.decode("ascii") 
       
      #JWT CREATION 
      #Either the x5t or kid claim must be present in the JWT assertion header (no need for both) 
      #but this example will include both for demonstration purpose 
      header = { 
          "alg":signing_alg, 
          "typ":"JWT", 
          "kid":certAlias, 
          "x5t":x5t 
      } 
      user_payload = { 
          "sub":userName, 
          "iss":clientId, 
          "aud":audience, 
          "iat":tokenIssued, 
          "exp":tokenExpiry, 
          "jti":tokenJti 
      } 
      encoded_user_assertion = jwt.encode( 
          payload=user_payload, 
          headers=header, 
          key=private_key, 
          algorithm=signing_alg) 
       
      #IDCS OAUTH REQUEST HEADER AND PAYLOAD CREATION USING CLIENT ID AND CLIENT SECRET 
      headers = { 
        'Content-Type':'application/x-www-form-urlencoded', 
        'Authorization':'Basic ' + clientIdSecret_base64_string 
      } 
      payload = { 
      'grant_type':'urn:ietf:params:oauth:grant-type:jwt-bearer', 
      'scope':scope, 
      'assertion':encoded_user_assertion 
      } 
       
      #IDCS OAUTH TOKEN REQUEST EXECUTION 
      response = requests.request("POST", idcs_url, headers=headers, data=payload) 
       
      #SCRIPT OUTPUT 
      print() 
      print() 
      print() 
      print('***************************************************************') 
      print('JWT OAUTH TOKEN GENERATION TEST') 
      print('***************************************************************') 
      print() 
      print('******************************') 
      print('ENVIRONMENT') 
      print('******************************') 
      print('OAUTH TOKEN GENERATION FOR USER:') 
      print(userName) 
      print() 
      print('******************************') 
      print('VARIABLES') 
      print('******************************') 
      print('TOKEN EXPIRY DURATION:') 
      print(str(expiryDuration) + ' seconds') 
      print() 
      print('TOKEN JTI:') 
      print(tokenJti) 
      print() 
      print('ENCODED CLIENTID:SECRET:') 
      print(clientIdSecret_base64_string) 
      print() 
      print('IDCS OAUTH TOKEN ENDPOINT:') 
      print(idcs_url) 
      print() 
      print('P12 PRIVATE KEY:') 
      print(private_key) 
      print() 
      print('ENCODED JWT USER ASSERTION:') 
      print(encoded_user_assertion) 
      print() 
      print('******************************') 
      print('IDCS OAUTH TOKEN REQUEST HEADERS AND PAYLOAD') 
      print('******************************') 
      print('REQUEST HEADERS:') 
      print(headers) 
      print() 
      print('REQUEST PAYLOAD:') 
      print(payload) 
      print() 
      print('******************************') 
      print('OAUTH TOKEN RESPONSE') 
      print('******************************') 
      print(response.json()) 
      print()

Example using Java Code

For an example of using Java Code to create a JWT token for an assertion grant type, see: https://www.ateam-oracle.com/post/creating-a-jwt-token-for-an-assertion-grant-type-flow

For an example of using Java Code to create a JWT token for Oracle IDCS, see: https://www.ateam-oracle.com/post/create-a-jwt-token-in-java-for-oracle-idcs