Secure your OIC Integration using OAuth Security Policy
Security is the key aspect in any implementation, specially when it comes to publish your API/Integration to external consumer outside of your organization.
Oracle Integration Cloud has capability to design an Integration which could be shared across internal/external Organisation. In this scenario security is paramount to protect that endpoint which will be published through OIC.
By default, if your OIC Integration has REST Endpoint with Trigger role, it’s offering Basic Authentication Or OAuth 2.0 Or both Security policy.
With Invoke role there are many other flavours of OAuth grant type which I won’t be covering in this blog. I am more focused on this blog for OAuth 2.0 for OIC Rest Adapter for trigger role only.
My objective of this blog to show you how you can protect OIC Integration Endpoint using OAuth Token, instead of using Basic username and Password as security policy.
This blog is the further extension of Oracle official document on topic Authentication Support, especially focused on JWT User Assertion OAuth 2.0 Grant type. There is another very useful blog published by Oracle PM team on same topic which helped me to do this POC and further extend on this topic with more detailed explanation and snapshots.
In this scenario, an external consumer will be invoking an OIC Integration which is been protected by OAuth 2.0 Security Policy by passing a valid OAuth token as Authorization parameter.
In this approach Sending an OAuth access token in the header while invoking an Oracle Integration endpoint after acquiring the access token from Oracle Identity Cloud Service that serves as the OAuth authorization provider. To know more about IDCS please refer this doc. IDCS is here act as Authorization server for OIC REST Endpoint call.
Below are the high-level steps for this configuration –
· Update default OIC IDCS application settings for Refresh token, Scope, role etc.
· Generate a new Self-signed Certificate with key-pair
o Generate a keystore with self-signed cert with key-pair
o Export Self-signed cert out from keystore
o Convert existing Keystore to P12 format keystore
o Export private Key from P12 keystore
· Create an “Confidential Application” IDCS App for OAuth
· Generate JWT User Assertion Token and OAuth token using shell-script
· Design & Deploy Hello World OIC Integration with REST Endpoint (Trigger Role)
· Test Integration using Postman by passing valid OAuth Token
· Monitor the Instance in OIC console for successful invocation
Update default OIC IDCS application settings for Refresh token, Scope, role etc.
Oracle Identity Cloud service is the foundation for all iPaaS offering in Oracle Cloud Infrastructure. When we provision OIC environment, associated default IDCS app also gets created in backend where we add/delete users and roles to allow/denied user’s access to OIC instance. You can find OIC IDCS app inside IDCS Console.
Navigate to OCI Console >> Identity & Service >> Federation >> click on “OracleIdentitycloudService”
Above step will bring the IDCS URL further click on that –
This will bring you at IDCS Console >> Oracle Cloud Services >> type the initials of your OIC environment in search window e.g. if your OIC Instance name “SE-OIC…” then type “SE” and hit search button and pick the one from result for which you are trying this OAuth config setup.
For selected IDCS application -
- Verify that the Is Refresh Token Allowed option is enabled
- Check under the Configuration > Resources section of the application about predefined scope (urn:opc:resource:consumer::all), it should be there if not you have to add that.
Next, make sure the users what you using to perform this configuration, must allowed/added under pre-defined “Service Role”. He should be member of this pre-defined role. If not, then add that user.
That’s it. OIC default IDCS App configuration done and dusted. Next, we will be moving to generate a keystore with self-signed cert with key-pair.
Generate a new Self-signed Certificate with key-pair
In order to setup this OAuth configuration, we need a Self-Signed cert with Key-pair (Public and Private) Keys.
Generate a keystore with self-signed cert with key-pair
When you run below command it will ask so many things e.g. First name, last name, organization, City, country etc. etc. Keep specifying appropriate value, accordingly it will generate keystore file which will be having Public, Private Keys and Self Signed Certificate. In this section we will be using Keytool & openssl command and various parameters associates with those commands. To know more about Keytool command, please refer product documentation. And Onenssl documentation.
##Syntex
keytool -genkey -keyalg RSA -alias <your_alias> -keystore <keystore_file> -storepass <password> -validity 365 -keysize 2048
##example
keytool -genkey -keyalg RSA -alias assert -keystore /Users/manishkumargupta/.ssh/oic_oauth_poc/oAuthKeystore.jks -storepass Welcome1 -validity 1825 -keysize 2048
Export self-singed cert out from keystore
In previous section, we have generated keystore file with Public, Private Key and Self-Signed Certificate, now we will extracting self-signed certificate out of keystore using below command.
##Syntex
keytool -exportcert -alias <your_alias> -file <filename> -keystore <keystore_file> -storepass <password>
keytool -exportcert -alias assert -file /Users/manishkumargupta/.ssh/oic_oauth_poc/assert.cer -keystore /Users/manishkumargupta/.ssh/oic_oauth_poc/oAuthKeystore.jks -storepass Welcome1
Convert existing Keystore to P12 format Keystore
Since keytool generate java based keystore but later we need more generic format of keystore called P12. It’s an alternate extension for what is generally referred to as a "PFX file", it's the combined format that holds the private key and certificate and is the format most modern signing utilities use.
##Syntex
keytool -importkeystore -srckeystore <filename> -srcstorepass <password> -srckeypass <password> -srcalias <your_alias> -destalias <your_alias> -destkeystore <destFileName> -deststoretype PKCS12 -deststorepass <password> -destkeypass <password>
##example
keytool -importkeystore -srckeystore /Users/manishkumargupta/.ssh/oic_oauth_poc/oAuthKeystore.jks -srcstorepass Welcome1 -srckeypass Welcome1 -srcalias assert -destalias destassert -destkeystore /Users/manishkumargupta/.ssh/oic_oauth_poc/destassert.p12 -deststoretype PKCS12 -deststorepass Welcome1 -destkeypass Welcome1
Export private Key from P12 keystore
Now, since we got P12 format based keystore, we need to extract private key out of this store which we will be using in next step to sign the JWT Token Assertion.
##Syntex
openssl pkcs12 -in <destFileName> -nodes -nocerts -out <pem_file>
##example ## This should show a success message: MAC verified OK
openssl pkcs12 -in /Users/manishkumargupta/.ssh/oic_oauth_poc/destassert.p12 -nodes -nocerts -out oic-oauth-pkey.pem
So, now we have self-signed certificate and private key exported out of keystore and ready to use in further steps.
Create an “Confidential Application” IDCS App for OAuth
Note:
For this step, if you not IDCS Administrator, there are high chances you will not be having rights to create a confidential IDCS App. If that’s the case, engage your IDCS Administrator and ask him to create a confidential IDCS App and perform below configuration.
In the Oracle Identity Cloud Service Console, go to the Application section to create a new application that allows you to trigger an integration with OAuth.
The application is added as a confidential application.
Specify the name and description whatever you like –
Inside Configuration tab page >> Client Configuration >> enable “JWT Assertion” and “Refresh token” grant type,
Set Client type = “Trusted” and Import “assert.cer” file (Contains Self-Signed Cert) which got generated as part of this section heading “Export Self-Signed certificate from keystore”,
set token insurance policy to “specific”
Click Add Scope under “Resources” section.
Add the scope containing urn:opc:resource:consumer::all, and click >.
The scope containing urn:opc:resource:consumer::all is added.
Overall configuration looks like this-
Save your changes. Skip the rest of the wizard steps and save the application. Activate the application. Once application gets activated you will see Client ID and Client Secret under configuration tab page >> general information section.
Make a note of Client Id and Client secret you will needed these stuff at later configuration.
Note: Above screenshot doesn’t show Secret Code coz my access to IDCS was limited, but IDCS Administrator can see both Client ID and Client Secret. So, ask your IDCS Admin to share both value with you.
Generate JWT User Assertion Token and OAuth Access token using shell-script
This is where complexity gets started. This is most prescriptive steps in official documentation where most of developer struggle, which is not explained in greater details, as its referring to use open source jjwt https://github.com/jwtk/jjwt library to generate the JWT user assertion.
There are two sub section in this heading-
1) we need to generate signed JWT token
2) Using that singed JWT token we need to generate OAuth Access token from IDCS which will be used to invoke OIC REST Endpoint.
As we need to generate a Signed JWT Token. JWT stand for JSON Web Token is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. More Info about JWT can be found here.
JSON Web Tokens consist of three parts separated by dots (.), which are:
- Header
- Payload
- Signature
Therefore, a JWT typically looks like the following.
xxxxx.yyyyy.zzzzz
Let's break down the above three different parts.
Header
The header typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.
In our case we will be using
{
"alg": " RS256",
"typ": "JWT"
}
Payload
The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically, the user) and additional data.
An example payload could be:
payload:
{
"sub": "manish.ku.gupta@oracle.com",
"jti": "8c7df446-bfae-40be-be09-0ab55c655436",
"iat": 1589889699,
"exp": 1589909699,
"iss": "d702f5b31ee645ecbc49d05983aaee54",
"aud": "https://identity.oraclecloud.com/"
}
Where:
- sub - specifies the user name for whom user assertion is generated. E.g. it’s my email id manish.ku.gupta@oracle.com which I used to login into my OCI account.
- jti - is a unique identifier and its fixed. I didn’t changed value of this unique code.
- iat - is epoch seconds. In computing, an epoch is a date and time from which a computer measures system time. Most computer systems determine time as a number representing the seconds removed from particular arbitrary date and time.
- exp - is the token expiry (epoch seconds).
- Iss - IDCS apps client ID.
- aud – is for Audience- must include the Oracle Identity Cloud Service audience https://identity.oracle.com/. The signing algorithm must be RS256.
Signature
- kid - specifies the key to use to verify the signature. Therefore, it must match with the uploaded certificate alias name in Oracle Identity Cloud Service
Now, once you understand above format of JWT token and meaning of each parameter, next steps to generate the JWT token.
To simply that process, below shell script would be helpful.
Disclaimer. This script is just for educational purpose. There are any many ways to generate signed JWT token, using this shell script we have tried to simplify the process but that doesn’t mean, this is the only process or this script can’t be modify and extended. Thanks, Vernon Saldanha, for sharing this script which you modify for your environment and generate signed JWT token.
In below script changed the value of APP_ID, SUB, KID, EXP (token expiry time whatever you want), SIGNATURE, <Name of your private key file>, IDCS URL, Client ID, Client Secret, Scope
Script which helps to generate JWT Assertion
#!/usr/bin/env bash # This script uses a private_key.pem to sign the payload which should be stored in the same location as the script itself. # Feel free to modify as needed base64url() { # Don't wrap, make URL-safe, delete trailer. base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n' } echo "--------------------------------" APP_ID="83a0fb081ef04f088ca17d8b1e9xxxx" AUD="https://identity.oraclecloud.com/" SUB="manish.ku.gupta@oracle.com" JTI="8c7df446-bfae-40be-be09-0ab55c655436" KID="assert" NOW=$( date +%s ) IAT="${NOW}" # expire 9 minutes in the future. EXP=$((${NOW} + 540)) HEADER_RAW='{"alg":"RS256","typ":"JWT","kid":"'${KID}'"}' HEADER=$(echo -n "${HEADER_RAW}" | base64url) echo ${HEADER_RAW} PAYLOAD_RAW='{"sub":"'${SUB}'","jti":"'${JTI}'","iat":'"${IAT}"',"exp":'"${EXP}"',"iss":"'${APP_ID}'","aud":"'${AUD}'"}' PAYLOAD=$(echo -n "${PAYLOAD_RAW}" | base64url ) echo ${PAYLOAD_RAW} HEADER_PAYLOAD="${HEADER}"."${PAYLOAD}" echo ${HEADER_PAYLOAD} echo "--------------------------------------" SIGNATURE=$(echo -n "${HEADER_PAYLOAD}" | openssl dgst -sha256 -binary -sign oic-oauth-pkey.pem | base64url) echo ${SIGNATURE} echo "--------------------------------------" JWT="${HEADER_PAYLOAD}"."${SIGNATURE}" echo ${JWT} #at this point you got the JWT assertion token, which will be used to get OAuth token from IDCS which will be used to invoke OIC REST endpoint. Now modify IDCS URL, Client ID, Client Secret, Scope and run shell script and you will get access token. echo "---- Get Access Token ----------------" IDCS_URL="https://idcs-5dfcc4d6691c419aad5d4ccb1xxxxxxx.identity.oraclecloud.com/oauth2/v1/token" ContentType="application/x-www-form-urlencoded" ClientID="83a0fb081ef04f088ca17d8xxxx" ClientSecret="4bb76a56-9c00-49f0-95c2-2exxxxxf" Scope="https://5F208232C8D5408ABCFxxxxE.integration.ocp.oraclecloud.com:443urn:opc:resource:consumer::all" base64Creds=$(echo -n "${ClientID}":"${ClientSecret}" | base64url ) granttype="urn:ietf:params:oauth:grant-type:jwt-bearer" access_token=$(curl -L -X POST ''${IDCS_URL}'' -H 'Content-Type: '${ContentType}'' -H 'Authorization: Basic '${base64Creds}'' --data-urlencode 'grant_type='${granttype}'' --data-urlencode 'assertion='${JWT}'' --data-urlencode 'scope='${Scope}'') echo ${access_token}
create a shell script in your system. Copy & paste above script into your script file, change the permission e.g. chmod 544 <your script file name> and run the script. It will generate OAuth access token which we can use to invoke OIC REST Endpoint.
This script is doing two steps, first its generating a valid JWT signed token and then using that JWT Signed token its generating another valid OAuth token. Copy the token value as it would be needed to pass as Authorization parameter inside Postman.
Design & Deploy Hello World OIC Integration with REST Endpoint (Trigger Role)
Create REST Connection with Trigger Role + OAuth Security Policy
OIC Console >> Integration >> Connection >> REST Adapter >> Specify the Name and Description and hit create button.
For security role select Trigger only and Security Policy OAuth 2.0
Create Hello World Integration and activate the same
Navigate to OIC Home >> Integration >> Integration >> Create >> Select “App Driven “ Integration Style and design basic Integration where REST Endpoint will receive name and will log that name into activity stream using logger activity.
Test Integration using Postman by passing valid OAuth Token
Once Integration gets activated, use the Rest Endpoint, and pass appropriate parameters. For OAuth token, go to Authorization section and paste the OAuth token value which you have copied after running the shell script and hit send button, you will see 200 http status code.
Monitor the Instance in OIC console for successful invocation
So, as you can see how I have configured a confidential IDCS App, enabled JWT Assertion and Refresh token grant type, imported the Self signed certificate, then used that IDCS apps client ID and Secret and many other parameters to generate JWT Signed token which has valid header and payload and further used that token to generate OAuth token which finally passed to OIC as Authorization parameter to invoke REST Integration.
I hope this blog will help you to implement OAuth security mechanism for Inbound OIC Integration.
Happy blogging
No comments:
Post a Comment