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