Using JWT User Assertion
To enable and use signed user assertions, you must:
- Generate a signed, encoded JWT user assertion using the private key that corresponds with the public certificate uploaded to the confidential application. There are several libraries available to generate and sign a JWT user assertion available here: https://jwt.io/libraries
- Generate the access token.
For requirements for the JWT header and payload, refer to your identity provider's documentation.
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 in your Oracle IAM confidential application. Choose either to use a KID or x5t. You do not need to use both. |
x5t |
Base64 URL encoded X.509 certificate SHA-1 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 Primavera Data Service, use
JWT .
|
alg | The algorithm identifies the specific type of JWT signing algorithm being used. For Primavera Data Service, use
RS256 .
|
The body must include the following claims:
Name | Value |
sub | The Primavera Data Service account user name. |
iss |
The client ID of the confidential application. See: Prerequisite Setup |
aud | The audience defines the recipients for which the JWT is intended. For Primavera Data Service, 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":"Primavera_Data_Service_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
Example using Python
The variables in this example must be replaced with the following information:
- <identity_domain_url>: The URL of your OCI identity domain.
- <identity_domain_confidential_application_clientID>: The client ID of the confidential application in your OCI identity domain.
-
<base64_encoded_clientID:client_secret>: The Base64 encoded client ID and client secret provided by your identity provider.
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' idp_url = 'https://<identity_domain_url>' + tokenEndpoint clientId = '<identity_domain_confidential_application_clientID>' clientSecret = '<base64_encoded_clientID:client_secret>' 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 and 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 identity provider's 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) #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 } #OAUTH TOKEN REQUEST EXECUTION response = requests.request("POST", idp_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('IDENTITY PROVIDER 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