<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[JWT with Oracle APEX]]></title><description><![CDATA[JWT with Oracle APEX]]></description><link>https://christianhesse.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 09 Sep 2026 09:10:14 GMT</lastBuildDate><atom:link href="https://christianhesse.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[JSON Web Tokens (JWT) with Oracle APEX]]></title><description><![CDATA[Introduction
JSON Web Tokens (JWT) have gained popularity for authentication and authorization in web applications and APIs. It’s a standardized (RFC 7519) and secure way to identify users and prevent unauthorized access. Many SaaS provider such as S...]]></description><link>https://christianhesse.hashnode.dev/json-web-tokens-jwt-with-oracle-apex</link><guid isPermaLink="true">https://christianhesse.hashnode.dev/json-web-tokens-jwt-with-oracle-apex</guid><category><![CDATA[JWT token,JSON Web,Token,Token authentication,Access token,JSON token,JWT security,JWT authentication,Token-based authentication,JWT decoding,JWT implementation]]></category><category><![CDATA[RSA Encryption]]></category><category><![CDATA[#oracle-apex]]></category><dc:creator><![CDATA[Christian Hesse]]></dc:creator><pubDate>Tue, 18 Nov 2025 11:15:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763464044431/4b51adb1-3cba-4200-a402-4994ca9b0a8f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction">Introduction</h3>
<p>JSON Web Tokens (JWT) have gained popularity for authentication and authorization in web applications and APIs. It’s a standardized (<a target="_blank" href="https://www.rfc-editor.org/rfc/rfc7519">RFC 7519</a>) and secure way to identify users and prevent unauthorized access. Many SaaS provider such as Salesforce, Snowflake, Atlassian, Twilio, Shopify etc. support JWT based authentications flows. The security aspect is provided by cryptographic methods that allow signing and optional encryption of the token. This article will focus on <em>signing</em> a JWT. For details about JWT encryption find more in this <a target="_blank" href="https://auth0.com/docs/secure/tokens/access-tokens/json-web-encryption">article</a>.</p>
<p>The signing can be done with:</p>
<ul>
<li><p>HMAC (Hash-based Message Authentication Code)</p>
</li>
<li><p>RSA or ECDSA (Asymmetric cryptographic algorithms)</p>
</li>
</ul>
<p>HMAC is a symmetric approach in which both client and server share the same secret key while RSA is an asymmetric method with a public and private key where the private key is used to sign the token and the public key to verify it.</p>
<p>A JWT is typically composed of three parts divided by dots “.” :</p>
<p><strong>HEADER . PAYLOAD . SIGNATURE</strong></p>
<p>The header contains just meta data about the used cryptographic method:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"alg"</span>: <span class="hljs-string">"HS256"</span>,
  <span class="hljs-attr">"typ"</span>: <span class="hljs-string">"JWT"</span>
}
</code></pre>
<p>The payload contains the claims like user id, issuer, expiration time etc. The payload should never contain sensitive information like passwords as in case of an unencrypted JWT the payload is not protected. More details can be found in the <a target="_blank" href="https://en.wikipedia.org/wiki/JSON_Web_Token">Wiki</a>.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"sub"</span>: <span class="hljs-string">"user-id"</span>,
    <span class="hljs-attr">"iss"</span>: <span class="hljs-string">"https://salesforce.com"</span>,
    <span class="hljs-attr">"iat"</span>: <span class="hljs-number">1615370644</span>,
    <span class="hljs-attr">"exp"</span>: <span class="hljs-number">1615374184</span>
}
</code></pre>
<p>Header and payload are then Base64 encoded.</p>
<p>So the header becomes “eyJhbGciOiAiSFMyNTYiLCJ0eXAiOiAiSldUIn0” and the playload “eyJzdWIiOiJ1c2VyLWlkIiwiaXNzIjoiaHR0cHM6Ly9zYWxlc2ZvcmNlLmNvbSIsImlhdCI6MTYxNTM3MDY0NCwiZXhwIjoxNjE1Mzc0MTg0fQ“.</p>
<p>The signature is created with header and payload Base64 encoded. All three parts form the JWT which can be used to authenticate against an API.</p>
<h3 id="heading-the-problem">The problem</h3>
<p>Oracle APEX provides a PL/SQL package which deals with JSON Web Tokens. It’s called <strong>APEX_JWT</strong>. If you open the <a target="_blank" href="https://docs.oracle.com/en/database/oracle/apex/24.2/aeapi/APEX_JWT.html">documentation</a> a note is hard to miss:</p>
<blockquote>
<p>APEX_JWT APIs only support HS256 symmetric encryption algorithm for claim signatures. Asymmetric encryption algorithms such as RS256 are not supported.</p>
</blockquote>
<p>If your API supports HS256 cryptographic algorithm for signing the JWT you can use the <strong>apex_jwt.encode()</strong> function. The result is a signed JWT (Note: in the documentation for this <a target="_blank" href="https://docs.oracle.com/en/database/oracle/apex/24.2/aeapi/ENCODE.html">function</a> it is written “<em>… function encodes and optionally encrypts payload.</em>” however the JWT is signed but not encrypted, so the payload is still pure Base64 encoded!)</p>
<p>Many API providers (e.g. <a target="_blank" href="https://docs.snowflake.com/en/developer-guide/snowflake-rest-api/authentication#label-sfrest-api-authenticating-key-pair">Snowflake SQL REST API</a>) require a (stronger) RS256 cryptographic algorithm for which the Oracle APEX PL/SQL API does not support us. There is a <a target="_blank" href="https://apexapps.oracle.com/pls/apex/r/apex_pm/ideas/details?idea=FR-3450">feature request</a> for APEX with status “<em>currently on the roadmap for a future release</em>” however even with currently newest APEX version 24.2 it is still not supported. Connor McDonald stated on <a target="_blank" href="https://asktom.oracle.com/ords/asktom.search?tag=rs256">Ask Tom</a> recently:</p>
<blockquote>
<p>But ultimately, APEX_JWT is an API layer built on top of the DBMS_CRYPTO package, which in 19c does not full public key support, and hence RS256 is not available.  </p>
<p>DBMS_CRYPTO does get that support in 21 and 23ai, but 99% of customers are still on 19c.  </p>
<p>Of course, there has been 28 RUs of 19c and hence DBMS_CRYPTO has continued to evolve in 19c as well, so hopefully it will come in due course to APEX.</p>
</blockquote>
<h3 id="heading-a-solution">A solution</h3>
<p>As java is a highly integrated programming language in Oracle Database my idea was to create a java class to create a JWT using RS256 cryptographic algorithm.</p>
<p>Here my implementation (<em>JwtGenerator.java</em>):</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> orajwt;

<span class="hljs-keyword">import</span> java.util.Base64;
<span class="hljs-keyword">import</span> java.security.*;
<span class="hljs-keyword">import</span> java.security.spec.*;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JwtGenerator</span> </span>{

    <span class="hljs-comment">/**
     * Create a JSON Web Token (JWT) and sign it with an RSA256 private key
     * <span class="hljs-doctag">@param</span> iss Issuer of the token
     * <span class="hljs-doctag">@param</span> sub Subject of the token
     * <span class="hljs-doctag">@param</span> iat Issue time for the JWT in UTC (equals current time value as seconds)
     * <span class="hljs-doctag">@param</span> exp Expiration time for the JWT in UTC (seconds)
     * <span class="hljs-doctag">@param</span> privateKey RSA256 private key used to sign the token
     * <span class="hljs-doctag">@return</span> The generated JWT as String
     * <span class="hljs-doctag">@throws</span> Exception
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">rs256Encode</span><span class="hljs-params">(String iss, String sub, Long iat, Long exp, String privateKey)</span> 
            <span class="hljs-keyword">throws</span> Exception </span>{

        String header = <span class="hljs-string">"{\"alg\":\"RS256\",\"typ\":\"JWT\"}"</span>;
        String headerEncoded = Base64.getUrlEncoder()
                                     .withoutPadding()
                                     .encodeToString(header.getBytes(<span class="hljs-string">"UTF-8"</span>));

        String payload = <span class="hljs-string">"{\"iss\":\""</span> + iss + <span class="hljs-string">"\",\"sub\":\""</span> + sub + <span class="hljs-string">"\",\"exp\":"</span> + exp + <span class="hljs-string">",\"iat\":"</span> + iat + <span class="hljs-string">"}"</span>;
        String payloadEncoded = Base64.getUrlEncoder()
                                     .withoutPadding()
                                     .encodeToString(payload.getBytes(<span class="hljs-string">"UTF-8"</span>));

        <span class="hljs-comment">// private key needs to get rid of begin/end blocks and any whitespace</span>
        privateKey = privateKey.replace(<span class="hljs-string">"-----BEGIN PRIVATE KEY-----"</span>, <span class="hljs-string">""</span>)
                               .replace(<span class="hljs-string">"-----END PRIVATE KEY-----"</span>, <span class="hljs-string">""</span>)
                              .replaceAll(<span class="hljs-string">"\\s+"</span>,<span class="hljs-string">""</span>);

        String jwt = headerEncoded + <span class="hljs-string">"."</span> + payloadEncoded;

        <span class="hljs-comment">// generate the signature for the JWT</span>
        <span class="hljs-keyword">byte</span>[] decodedKey = Base64.getDecoder().decode(privateKey);

        PKCS8EncodedKeySpec keySpec = <span class="hljs-keyword">new</span> PKCS8EncodedKeySpec(decodedKey);
        KeyFactory kf = KeyFactory.getInstance(<span class="hljs-string">"RSA"</span>); 
        PrivateKey rsaPrivateKey = kf.generatePrivate(keySpec);

        Signature privateSignature = Signature.getInstance(<span class="hljs-string">"SHA256withRSA"</span>);
        privateSignature.initSign(rsaPrivateKey);

        privateSignature.update(jwt.getBytes(<span class="hljs-string">"UTF-8"</span>));
        String signatureEncoded = Base64.getUrlEncoder()
                                 .withoutPadding()
                                 .encodeToString(privateSignature.sign());

        <span class="hljs-keyword">return</span> jwt + <span class="hljs-string">"."</span> + signatureEncoded;
    }
}
</code></pre>
<p>The rs256Encode() method supports standard JWT claims “<em>iss</em>”, “<em>sub</em>”, “<em>iat</em>” and “<em>exp</em>”.</p>
<p>To upload the class file to Oracle Database a utility named <a target="_blank" href="https://docs.oracle.com/en/database/oracle/oracle-database/19/jjdev/loadjava-tool.html"><strong>loadjava</strong></a> exists. I took my favorite IDE Eclipse compiled the java file and exported the class file to a jar file. Make sure to compile the java file for the destination JVM running in the database. In my case it was Java8 running on 19c. Then I could run load java:</p>
<pre><code class="lang-plaintext">loadjava -u user@database -v -r orajwt-1.0.jar
</code></pre>
<p>To verify the loaded java class in the database run this SQL command:</p>
<pre><code class="lang-plaintext">SELECT object_name, object_type, status FROM all_objects WHERE object_type like '%JAVA%' and lower(object_name) like '%orajwt%';
</code></pre>
<p>The status should be “<em>VALID</em>”.</p>
<p>Next create a PL/SQL wrapper function to access the java method from PL/SQL:</p>
<pre><code class="lang-sql">   <span class="hljs-comment">/** 
    * Create JSON Web Token (JWT). 
    * This is a PL/SQL wrapper function for java method "JwtGenerator.rs256Encode" from orajwt package
    * @param p_iss The issuer of the token
    * @param p_sub The subject of the token
    * @param p_iat Issue time for the JWT in UTC (equals current time value as seconds) 
    * @param p_exp Expiration time for the JWT in UTC (seconds)
    * @param p_private_key RSA256 private key for signing the JWT
    * @return The JWT as String
    */</span>
    function create_JWT(
       p_iss         in varchar2,
       p_sub         in varchar2,
       p_iat         in number,
       p_exp         in number,
       p_private_key in varchar2
    ) return varchar2 as language java name 'orajwt.JwtGenerator.rs256Encode(java.lang.String, java.lang.String, java.lang.Long, java.lang.Long, java.lang.String) return java.lang.String';
</code></pre>
<p>To create a RSA256 signed JSON Web Token run create_JWT() from PL/SQL, provide the claims and the private key.</p>
<p>This blog post was focusing on signing a JWT using RSA256 encryption algorithm and a private key. Next is to add a function to verify a signed JWT using the public key. For this also a PL/SQL implementation exists from <a target="_blank" href="https://ilmarkerm.eu/blog/2023/10/validating-jwt-tokens-with-rs256-signatures-in-pl-sql/">Ilmar Kerm</a>.</p>
]]></content:encoded></item></channel></rss>