1- /*
2- Largely inspired by Tech Note: "JSON Web Tokens in 4D"
3- See: https://kb.4d.com/assetid=79100
4- */
5-
61property _header : Object
72property _payload : Object
3+ property _cryptoKey : 4D.CryptoKey
84
9- Class constructor ()
5+ Class constructor ($inParam : Variant )
106
117 This ._header := {}
128 This ._payload := {}
139
10+ Case of
11+ : ((Value type ($inParam )= Is object) && OB Instance of ($inParam ; 4D.CryptoKey))
12+ This ._cryptoKey := $inParam
13+
14+ : ((Value type ($inParam )= Is text) && (Length (String ($inParam ))> 0))
15+ This ._cryptoKey := Try (4D.CryptoKey.new({type: "PEM"; pem: $inParam})) // Use specified PEM format Key
16+
17+ Else
18+ This ._cryptoKey := Null
19+ End case
1420
1521 // Mark: - [Public]
1622 // ----------------------------------------------------
@@ -19,7 +25,7 @@ Class constructor()
1925Function decode ($inToken : Text) : Object
2026
2127 Case of
22- : ((Value type (inToken)# Is text) || (Length (String (inToken))= 0))
28+ : ((Value type ($ inToken )# Is text) || (Length (String ($ inToken ))= 0))
2329 This ._throwError (9 ; {which: "\" $inToken\" " ; function: "JWT.decode" })
2430
2531 Else
@@ -52,9 +58,6 @@ Function generate($inParams : Object; $inPrivateKey : Text) : Text
5258 : ((Value type ($inParams .payload )# Is object) || (OB Is empty ($inParams .payload )))
5359 This ._throwError (9 ; {which: "\" $inParams.payload\" " ; function: "JWT.generate" })
5460
55- : ((Value type ($inPrivateKey )# Is text) || (Length (String ($inPrivateKey ))= 0))
56- This ._throwError (9 ; {which: "\" $inPrivateKey\" " ; function: "JWT.generate" })
57-
5861 Else
5962 var $alg : Text:= ((Value type ($inParams .header .alg )= Is text) && (Length ($inParams .header .alg )> 0)) ? $inParams .header .alg : "RS256"
6063 var $typ : Text:= ((Value type ($inParams .header .typ )= Is text) && (Length ($inParams .header .typ )> 0)) ? $inParams .header .typ : "JWT"
@@ -84,9 +87,17 @@ Function generate($inParams : Object; $inPrivateKey : Text) : Text
8487
8588 // Generate Verify Signature Hash based on Algorithm
8689 If ($algorithm= "HS@")
87- $signature := This ._hashHS (This ; $inPrivateKey ) // HMAC Hash
90+ If ((Value type ($inPrivateKey )# Is text) || (Length (String ($inPrivateKey ))= 0))
91+ This ._throwError (9 ; {which: "\" $inPrivateKey\" " ; function: "JWT.generate" })
92+ Else
93+ $signature := This ._hashHS (This ; $inPrivateKey ) // HMAC Hash
94+ End if
8895 Else
89- $signature := This ._hashSign (This ; $inPrivateKey ) // All other Hashes
96+ If ((This ._cryptoKey = Null) && ((Value type ($inPrivateKey )# Is text) || (Length (String ($inPrivateKey ))= 0)))
97+ This ._throwError (9 ; {which: "\" $inPrivateKey\" " ; function: "JWT.generate" })
98+ Else
99+ $signature := This ._hashSign (This ; $inPrivateKey ) // All other Hashes
100+ End if
90101 End if
91102
92103 // Combine Encoded Header and Payload with Hashed Signature for the Token
@@ -106,9 +117,6 @@ Function validate($inJWT : Text; $inKey : Text) : Boolean
106117 : ((Value type ($inJWT )# Is text) || (Length (String ($inJWT ))= 0))
107118 This ._throwError (9 ; {which: "\" $inJWT\" " ; function: "JWT.validate" })
108119
109- : ((Value type ($inKey )# Is text) || (Length (String ($inKey ))= 0))
110- This ._throwError (9 ; {which: "\" $inKey\" " ; function: "JWT.validate" })
111-
112120 Else
113121 // Split Token into the three parts: Header, Payload, Verify Signature
114122 var $parts : Collection:= Split string ($inJWT ; "." )
@@ -132,15 +140,26 @@ Function validate($inJWT : Text; $inKey : Text) : Boolean
132140
133141 var $algorithm : Text:= This ._header .alg
134142 If ($algorithm= "HS@")
135- $signature := This ._hashHS ($jwt ; $key ) // HMAC Hash
136- return ($signature= $parts[2 ])
143+ If ((Value type ($inKey )# Is text) || (Length (String ($inKey ))= 0))
144+ This ._throwError (9 ; {which: "\" $inKey\" " ; function: "JWT.validate" })
145+ Else
146+ $signature := This ._hashHS ($jwt ; $key ) // HMAC Hash
147+ return ($signature= $parts[2 ])
148+ End if
137149 Else
138- // Prepare CryptoKey settings
139- var $settings : Object:= {type: "PEM" ; pem: $key } // Use specified PEM format Key
140- var $cryptoKey : 4D.CryptoKey:= 4D.CryptoKey.new($settings)
141- If ($cryptoKey# Null)
142- var $result : Object:= $cryptoKey .verify (String ($parts [0 ]+ "." + $parts [1 ]); $parts [2 ]; {hash: (Substring ($jwt ._header .alg ; 3 )= "256") ? SHA256 digest : SHA512 digest; pss: Bool ($jwt ._header .alg = "PS@" ); encoding: "Base64URL" })
143- return Bool ($result .success )
150+ If ((This ._cryptoKey = Null) && ((Value type ($inKey )# Is text) || (Length (String ($inKey ))= 0)))
151+ This ._throwError (9 ; {which: "\" $inKey\" " ; function: "JWT.validate" })
152+ Else
153+ // Prepare CryptoKey settings
154+ If ((Value type ($inKey )= Is text) && (Length (String ($inKey ))> 0))
155+ This ._cryptoKey := Try (4D.CryptoKey.new({type: "PEM"; pem: $key})) // Use specified PEM format Key
156+ End if
157+ If (This ._cryptoKey = Null)
158+ This ._throwError (15 ) // The private or public key doesn't seem to be valid PEM.
159+ Else
160+ var $result : Object:= This ._cryptoKey .verify (String ($parts [0 ]+ "." + $parts [1 ]); $parts [2 ]; {hash: (Substring ($jwt ._header .alg ; 3 )= "256") ? SHA256 digest : SHA512 digest; pss: Bool ($jwt ._header .alg = "PS@" ); encoding: "Base64URL" })
161+ return Bool ($result .success )
162+ End if
144163 End if
145164 End if
146165
@@ -218,36 +237,38 @@ Function _hashHS($inJWT : cs.NetKit.JWT; $inPrivateKey : Text) : Text
218237
219238Function _hashSign ($inJWT : cs .NetKit .JWT ; $inPrivateKey : Text) : Text
220239
221- var $hash ; $encodedHead ; $encodedPayload : Text
222- var $settings : Object
223- var $privateKey : Text:= ((Value type ($inPrivateKey )= Is text) && (Length ($inPrivateKey )> 0)) ? $inPrivateKey : ""
224-
225- // Encode Header and Payload to build Message
226- BASE64 ENCODE (JSON Stringify ($inJWT ._header ); $encodedHead ; * )
227- BASE64 ENCODE (JSON Stringify ($inJWT ._payload ); $encodedPayload ; * )
240+ var $hash : Text
228241
229- // Prepare CryptoKey settings
230- If (Length ($privateKey )= 0)
231- $settings := {type: "RSA" } // 4D will automatically create RSA key pair
242+ If ((This ._cryptoKey = Null) && ((Value type ($inPrivateKey )# Is text) || (Length (String ($inPrivateKey ))= 0)))
243+ This ._throwError (9 ; {which: "\" $inPrivateKey\" " ; function: "JWT.validate" })
232244 Else
233- $settings := {type: "PEM" ; pem: $privateKey } // Use specified PEM format Key
234- End if
235-
236- // Create new CryptoKey
237- var $cryptoKey : 4D.CryptoKey:= 4D.CryptoKey.new($settings)
238- If ($cryptoKey# Null)
245+ // Prepare CryptoKey settings
246+ If ((Value type ($inPrivateKey )= Is text) && (Length (String ($inPrivateKey ))> 0))
247+ This ._cryptoKey := 4D.CryptoKey.new({type: "PEM"; pem: $inPrivateKey}) // Use specified PEM format Key
248+ End if
239249
240- // Parse Header for Algorithm Family
241- var $algorithm : Text:= Substring ($inJWT ._header .alg ; 3 )
242- var $hashAlgorithm : Integer
243- If ($algorithm= "256")
244- $hashAlgorithm := SHA256 digest
250+ If (This ._cryptoKey # Null)
251+ var $encodedHead ; $encodedPayload : Text
252+
253+ // Encode Header and Payload to build Message
254+ BASE64 ENCODE (JSON Stringify ($inJWT ._header ); $encodedHead ; * )
255+ BASE64 ENCODE (JSON Stringify ($inJWT ._payload ); $encodedPayload ; * )
256+
257+ // Parse Header for Algorithm Family
258+ var $algorithm : Text:= Substring ($inJWT ._header .alg ; 3 )
259+ var $hashAlgorithm : Integer
260+ If ($algorithm= "256")
261+ $hashAlgorithm := SHA256 digest
262+ Else
263+ $hashAlgorithm := SHA512 digest
264+ End if
265+
266+ // Sign Message with CryptoKey to generate hashed verify signature
267+ $hash := This ._cryptoKey .sign (String ($encodedHead + "." + $encodedPayload ); {hash: $hashAlgorithm ; pss: Bool ($inJWT ._header .alg = "PS@" ); encoding: "Base64URL" })
245268 Else
246- $hashAlgorithm := SHA512 digest
269+ This . _throwError ( 15 ) // The private or public key doesn't seem to be valid PEM.
247270 End if
248271
249- // Sign Message with CryptoKey to generate hashed verify signature
250- $hash := $cryptoKey .sign (String ($encodedHead + "." + $encodedPayload ); {hash: $hashAlgorithm ; pss: Bool ($inJWT ._header .alg = "PS@" ); encoding: "Base64URL" })
251272 End if
252273
253274 return $hash
0 commit comments