Implement JWT Token Authentication with Fusion Cloud Application (REST/SOAP) & extend in Node.js based applications

December 6, 2023 | 15 minute read
Ranveer Tiwari
Principal Solution Architect | A-team
Dipak Chhablani
Principal Solutions Architect | A-Team - Cloud Solution Architects
Text Size 100%:

Introduction

Many of Oracle Cloud Application Customers have Integrations requirements with other internal/External Application & for calling these API’s different authentication methods are used.

There are broadly three mechanisms supported by Fusion SaaS as Access Control to API’s

  1. Basic authentication over SSL (Secure Socket Layer), which extracts the user name and password credentials from the HTTP header.
  2. SAML 2.0 bearer token in the HTTP header over SSL, which extracts a SAML 2.0 bearer assertion (XML security token), This is predominantly used for implementing SSO between web applications using identity systems or for SOAP based web services.
  3. JWT token in the HTTP header over SSL, which extracts the username from the JWT token, this is mostly used for API Authentication. For REST APIs, use JSON Web Tokens (JWT) for authentication. JWT Token can be used in 2-legged OAuth flow, in which an application uses client credentials to authenticate with IDCS to obtain a JWT token and then sends the token to the resource service for authentication and authorization, an access token (JWT token) and a refresh token to periodically replace the access token are used.

In this blog we will consider those customers who has only SaaS Subscription & not using PaaS IDCS – (where Customer can use OAuth2.0 token mechanism in which case, they need to create confidential application setup using IDCS service)

Customer who are not using PaaS IDCS, have only two options – basic Authentication & JWT Access tokens. Therefore, by this limitation sometimes customer often go by basic Authentication as access control mechanism. The use of Basic Authentication creates a significant risk of credential leakage as it passes weakly encoded usernames and password in every web request header.

Oracle SaaS Cloud supports JWT token-based authentication in its REST API’s and SOAP Webservices in addition to the basic authentication mechanism.

Register JWT Trust Issuer for API Authentication

 

Implementation Details:

Following Steps are required to implement JWT bearer token for API Authentication for SaaS.

Prerequisites -

  • OpenSSL - The certificate can be self-signed (for development only) or signed by a CA with optional intermediate certificate authorities.

* Required one time for certificate generation.

  • JDK – This will be used to run java program to generate token.

JWT Token Structure - 

JWTs consist of three parts separated by dots (.), which are:

  • Header
  • Payload
  • Signature

Therefore, a JWT typically looks like the following.

XXXX.YYYY.ZZZZ

Header

The header consists of three parts:

  • The signing algorithm being used, such as HMAC SHA256 or RSA
  • The token type, which is JWT
  • The x5t which is the base64 encoded fingerprint of the trusted issuer cert. (Sourced from your newly created X509 key pair)

Payload

The second part of the token is the payload, which contains the claims. Claims are statements about an entity (typically who the user is) and any additional data.

Signature

To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that. This is used as the Message Authentication Code (MAC).

 

Step 1: Generate a X.509 Key Pair

JWT Header : Here we are generating Self-Signed Certificate using RSA Private key, this has to be done by Fusion Application owner, for production instance it is recommended to use fully authorized key with a certificate from a well-known Certification Authority.

  1. Generate a private key (private.key)
openssl genrsa -out private.key 2048
  1. Using the created private key, create an X509 certificate (.cer file) containing your public key, this certificate is valid for 365 days.
openssl req -new -x509 -key private.key -out publickey.cer -days 365

In our example, we have used as below  -   

CN= <Fusion POD Name>,OU=cloud,O=oracle,L=Redwood,ST=CA,C=US

  1. Convert into pem format.
openssl rsa -in private.key > private_key.pem
  1. For signing convert Pem private key to Der format
openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -nocrypt > jwt.der
  1. Copy the publickey into pub.pem file
cp publickey.cer pub.pem

Step 2:  Java Program

Oracle JSON Web Token is a full Java solution that provides extensive support for JWT tokens, to use Oracle JSON Web Token, your system must have the Java Development Kit (JDK) version 1.6 or higher.

You can invoke this Java program from your third party/On-prem applications or use Serverless functions at OCI for cloud-based application.

Below are the JAR files required to support this Java program, please ensure they are included in java Class path or present in same folder where Java Program is kept.

Following files are part of Oracle® Security Developer Tools, OSDT libraries are installed with Oracle WebLogic Application Server in ORACLE_HOME, Oracle Security Developer Tools are java tools that enable you implement a wide range of security tasks and projects by using the cryptography standards and protocols.

WebLogic Server Installation can be downloaded from OTN, files location would be as below .

$ORACLE_HOME/oracle_common/modules/oracle.osdt/osdt_cert.jar

$ORACLE_HOME/oracle_common/modules/oracle.osdt/osdt_core.jar

$ORACLE_HOME/oracle_common/modules/oracle.osdt/osdt_restsec.jar

$ORACLE_HOME/modules/jackson-core-asl-1.9.13.jar

$ORACLE_HOME/modules/jackson-mapper-asl-1.9.13

Alternatively, you can search these JAR files from online maven repositories.

JWT Payload : The payload is constructed based on the following rules, You need to set custom claim parameters, Oracle HCM has the requirement for the following fields and values.

 

Abbreviation

Meaning

Description

iss

Issuer

Default to www.mycompany.com

prn

Principal

The username of user that has the Fusion Applications Integration privilege.

(in this example ‘XXX.YYYY@oracle.com)

aud

Audience

String or array of strings that identifies the recipients that the JWT is intended for.

Ex:

https://fa-<Fusion POD Name>-test-saasfaprod1.fa.ocs.oraclecloud.com
* This is Optional for Fusion Application.

iat

Issued At

Unix epoch time format of when the token was generated e.g ‘1596271154’ for 08/01/2020 @ 8:39am (UTC)

exp

Expiration

Unix epoch time format of when the token will expire e.g ‘1918081154’ for 10/13/2030 @ 12:19am (UTC)

 

Set the parameters like algorithm, issuer, expiry time, other claims and so on as below.

In our example -

jwtToken.setAlgorithm(JwtToken.SIGN_ALGORITHM.RS256.toString()
jwtToken.setIssuer("www.mycompany.com")
jwtToken.setSubject("XXX.YYYY@oracle.com")
jwtToken.setType(JwtToken.JWT)
jwtToken.setClaimParameter("aud","https://fa-<Fusion POD Name>-test-saasfaprod1.fa.ocs.oraclecloud.com")

 

Ensure the Trust Issuer matches the JWT issuer field you set in below Java Program
Aud is optional for Fusion Applications, however if we provide only then FA will Validate if JWT audience Is intended for right FA Application URL.

Refer Below Java Program  –


import java.io.FileInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;
import oracle.security.restsec.jwt.JwtToken;

public class GenerateJWT
{       public static void main(String[] args) throws Exception
        {
                String iss ="www.mycompany.com"; //JWT issuer -iss attribute
                String sub=" XXX.YYYY@oracle.com"; //JWT principalll -prn attribute
                JwtToken jwtToken = new JwtToken();
                //Fill in all the parameters- algorithm, issuer, expiry time, other claims etc
                jwtToken.setAlgorithm(JwtToken.SIGN_ALGORITHM.RS256.toString());
                jwtToken.setIssuer(iss);
                jwtToken.setSubject(sub);
                jwtToken.setType(JwtToken.JWT);
                jwtToken.setClaimParameter("aud","https://fa--test-saasfaprod1.fa.ocs.oraclecloud.com"); //this will set custom claim parameters,example "aud" is custom JWT claim with value "appsurl")
                //iat attribute-time when JWT was generated
                long nowMillis = System.currentTimeMillis();
                Date now = new Date(nowMillis);
                jwtToken.setIssueTime(now);
                //token expires in 10 minutes
                jwtToken.setExpiryTime(new Date(nowMillis + 10*60*1000));
                //x5t attribute,read the public key from pem format
                InputStream inStream = new FileInputStream("pub.pem");
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                X509Certificate publicKey = (X509Certificate)cf.generateCertificate(inStream);
                inStream.close();
                jwtToken.setX509CertThumbprint(publicKey);
                //for signing read private key in der format
                RandomAccessFile raf = new RandomAccessFile("jwt.der", "r");
                byte[] buf = new byte[(int)raf.length()];
                raf.readFully(buf);
                raf.close();
                PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(buf);
                KeyFactory kf = KeyFactory.getInstance("RSA");
                PrivateKey privateKey = kf.generatePrivate(kspec);
                // sign the token with a private key
                String jwtString = jwtToken.signAndSerialize(privateKey);
                System.out.println(jwtString);
        }
}

 

Step 3: Configure Token-based authentication within Oracle Cloud Fusion Application

  1. Login as security manager user and access the security console.

Navigator > [Tools] Security Console

  1. Click API Authentication.
  2. Click Create Oracle API Authentication Provider.
  3. On the newly opened Oracle API Authentication Provider Details page, click Edit and enter the following information.
  4. Trusted Issuer

    Name of the calling provider, in our case - www.mycompany.com

    * Ensure the Trust Issuer matches the JWT issuer field you set in Java Program

    Token Type

    JWT

  5. Click Save and Close.
  6. Next Select the Inbound API Authentication Public Certificates from the left-hand menu.
  7. Complete the Certificate Alias e.g. ORA_ASE_JWT_MYCOMPANY.
  8. Select Browse for the Import Public Certificate and navigate to the location of the publickey.cer file created in the earlier step.
  9. Select Done which will return you to the API Authentication overview page.

Screenshots

           Trusted Issuer -

           Trust Issuer

           Upload Certificate & Provide Alias Name - 

           Upload Certificate

You are now able to configure REST and SOAP services to use JWT to authenticate against your Oracle HCM instance.

Step 4: Generate Token

Execute Below Java Program as JWT.java -


#!/bin/bash
java -cp "./jackson-core-asl-1.9.13.jar:jackson-mapper-asl-1.9.13.jar:osdt_cert.jar:osdt_core.jar:osdt_restsec.jar" JWT.java

 

Step 5: Test & Verify

Once we complete above Setup & generate token & analyze decoded token in JWT.io, it shows following property :

Issuer                 - www.mycompany.com

User(principal) - XXX.YYYY@oracle.com

Aud                    - FA Application URL

JWT Token

Copy the JWT generated and test the rest API auth in Postman/SoapUI

REST:
REST Call


SOAP:

Add an HTTP header "Authorization" with the value of

Authorization: Bearer your-jwt-token-string

SOAP Call


Extend in Node.js Based Applications

Use case: Identity Propagation - This approach proves beneficial in scenarios where you have implemented Oracle Digital Assistant (excluding Hybrid) or a custom application with Node.JS as the backend. Additionally, if the ODA/custom application uses a different identity provider than IDCS, and you are seeking a solution to propagate Fusion user identity without synchronizing any users.

Self-Signed Certificate: The steps mentioned above remain applicable for generating a private key and certificate. However, Node.JS explicitly requires the certificate fingerprint. The following commands are tailored for Node.JS implementation.

 

  1. Generate a private key in. pem format.
openssl genpkey -algorithm RSA -out private_key.pem
  1. Using the created private key, create an X509 certificate (.crt file) containing your public key.
openssl req -x509 -newkey rsa:2048 -key private_key.pem -out public_certificate.crt

       In our example, we have provided following values for certificate parameters.

         CN=<Fusion POD Name>,OU=cloud,O=oracle,L=Redwood,ST=CA,C=US

  1. Generate the fingerprint from certificate.
openssl x509 -sha1 -in public_certificate.crt -noout -fingerprint
  1. Convert fingerprint into base 64 format.
echo "52:9F:CB:19:DB:8C:9E:6F:58:C2:3F:AF:B8:0A:75:3D:B9:1D:D9:2B"|xxd -r -p | base64

Upon executing the above steps, upload the public certificate to fusion application via security console as explained earlier. The private key and fingerprint in base64 format will be used in Node.JS application to sign the JWT token.

jsonwebtoken Library: The Node.JS application will utilize the jsonwebtoken library to generate and sign the JWT token as per Fusion application requirements. The demo app is using the following version of the library. You will need include the following library dependency (check for supported version) into the package.json file and execute npm install on the project folder to include into project.

version – "jsonwebtoken": "^9.0.2"

Demo APP: The JWT token consists of a header and payload, signed with a private key. The header includes attributes such as algorithm, type, and certificate fingerprint, while the payload contains subject, issuer, creation, and expiry information. The following code shows the creation of header and payload objects, concluding with the signing process with private key using the JSON Web Token library.
 


const jwt = require('jsonwebtoken');
const fs  = require('fs');
const key = fs.readFileSync('./keys/private_key.pem'); //private key path
const userName = "casey.brown"
var x5t = "Up/LGduMnm9Ywj+vuAp1Pbkd2Ss=";  //fingerprint
const alg = "RS256";  //algorithm

const headers = {
   "x5t": x5t,
   "typ": "JWT",
   "alg": alg
}

var date = new Date();
var iatSeconds = date.getTime();
var expSeconds = iatSeconds + (60*60); // set the JWT expiration to 1 hour. Can be longer
const payload= {
    "prn": userName,
    "sub": userName,    
    "iss": "www.mycompany.com", //should be same as per setup in security console
    "exp": expSeconds,
    "iat": iatSeconds
};

jwt.sign(payload, key, {header:headers}, function(err,token)
{
    if(err){
        console.log("error : "+err);
    }else {
        console.log("token : "+token);
    }
});

 

Generate Token: Navigate to the terminal window of the Node.JS application and execute the script by running the command node <filename.js>. The script will output the JWT token on the terminal through console log. You can test and verify the token as mentioned above in Step-5.

JWT Node JS Token

Conclusion

JWT Bearer token when decoded using JWT.io shows user name as - Principal as header which is used for identity propagation & remaining signature part is created by encoding the encoded header, the encoded payload, a secret, the algorithm specified in the header, and then signed which ensures REST or SOAP message wasn't changed during transmission.

Using JWT as API Authentication has more advantage over Basic Auth -

  1. Token expiry time – You can set your token expiry time (With Basic Authentication there is likelihood of stale sessions and session hijacking as it typically is not implemented with a logout or timeout feature),
  2. Certificate validity – You have more control how long certificate is valid & also can add this as trusted certificate in your domain.
  3. Audience (Right Intended Application) - You have control over where this token is consumed & for intended application.
  4. Unlike Basic Authentication – it doesn’t expose user credentials over headers in REST & SOAP calls.
  5. Fusion has a password expiration policy that requires user to change password at a certain cadence, these events require that the password be updated on the calling Integrations to FA, using JWT bearer token avoids this issue – Calling Applications generates a JWT using RSA private Key. X509 Certificate (Public Key) is registered in Fusion which can decrypt the JWT using the public key and extract the principal.

We hope with this blog you have more clear understanding of implementing JWT tokens in authentication of Oracle SaaS Applications API/Webservices.

References:

  1. Configure JWT Authentication Provider

https://www.oracle.com/webfolder/technetwork/tutorials/obe/fusionapps/HCM/JWT_API_Authentication_OBE/html/index.html

  1. Oracle JSON Web Token

https://docs.oracle.com/cd/E23943_01/security.1111/e10037/jwt.htm#CIHCEHJJ

  1. Create a Self-Signed Certificate Authority

https://www.ateam-oracle.com/post/create-a-self-signed-certificate-authority

  1. Create a JWT Token in Java for Oracle IDCS

https://www.ateam-oracle.com/post/create-a-jwt-token-in-java-for-oracle-idcs

  1. A Quick Note on Using JWT Token Authentication with Oracle SaaS API

https://www.ateam-oracle.com/post/a-quick-note-on-using-jwt-token-authentication-with-oracle-saas-api

  1. Creating a JWT Token for an Assertion Grant Type Flow

https://www.ateam-oracle.com/post/creating-a-jwt-token-for-an-assertion-grant-type-flow

  1. Developing Applications with Oracle Security Developer Tools

https://docs.oracle.com/cd/F17742_01/12.2.1.3/sdtrg/developing-applications-oracle-security-developer-tools.pdf

  1. Oracle Fusion Middleware WebLogic Installation

https://docs.oracle.com/cd/E28280_01/nav/im.htm

  1. MoS Note - How to Use JSON Web Token (JWT) For Authorization with Fusion Cloud Application REST APIs and SOAP Web Services? (Doc ID 2572018.1)

Ranveer Tiwari

Principal Solution Architect | A-team

Dipak Chhablani

Principal Solutions Architect | A-Team - Cloud Solution Architects


Previous Post

Fusion Applications Integration with Microsoft Teams : Introduction to Extending the Sample

Angelo Santagata | 1 min read

Next Post


Use OCI Object Storage as a Mounted Filesystem with Rclone

KC Flynn | 7 min read