Using JWT User Assertion

You must generate a signed, encoded JWT user assertion using the private key which corresponds to the public certificate uploaded to the confidential application.

To enable and use signed user assertions, you must:

  1. Generate a JWT user assertion.
  2. Generate the access token.

Requirements for the JWT header and payload are outlined in Oracle Cloud Platform REST Adapter documentation at 2 REST Adapter Concepts, Authentication Support, in the Use OAuth 2.0 Grants in Identity Domain Environments section. You can shortcut to the correct section by clicking the Prerequisites for JWT User Assertion link.

A user assertion includes a header, body, and signature.

The header must include the following attributes:

Name Value
kid

The key identifier identifies the trusted, third-party certificate for validating the assertion signature. The KID must match the certificateAlias of the public certificate.

Choose either to use a KID or x5t. You do not need to use both.

x5t

Base64 URL encoded X.509 certificate sha1 thumbprint. Used to identify the trusted third-party certificate to validate the assertion signature.

Choose either to use a x5t or KID. You do not need to use both.

type The type identifies the type of assertion. For this process, use JWT.
alg The algorithm identifies the specific type of JWT signing algorithm being used. For this process, use RS256.

The body must include the following claims:

Name Value
sub The subject is the P6 EPPM account username.
iss

The issuer is the client ID of the confidential application.

See: Prerequisite Setup

aud The audience defines the recipients for which the JWT is intended. For this process, use https://identity.oraclecloud.com.
exp The expiration time of the JWT assertion, specified in UNIX epoch time.
iat The date the assertion was issued, in UNIX epoch time.
jti The 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, concatenated with a dot, and 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(expiryDuration)  #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