So I've been working on using some Google authentication for a Uniface web application, and it's clever stuff. However, being the security conscious people that they are, they use a JSON Web Token (JWT) - pronounced "jot", apparently.
To quote the abstract...
The Google API documentation is pretty good. It gives you an endpoint that you can use to verify the token for debugging purposes, but suggests that in production you should be doing the verification locally...
I did look, of course I did, but did I expect to find Uniface on that list? No I did not. They have examples for .NET, Java, PHP, Python and Ruby. So this is me, trying it the hard way, in Uniface.
I should point out, there are a few caveats to this...
To quote the abstract...
The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS) and/or encrypted using JSON Web Encryption (JWE).
The Google API documentation is pretty good. It gives you an endpoint that you can use to verify the token for debugging purposes, but suggests that in production you should be doing the verification locally...
Fortunately, there are well-debugged libraries available in a wide variety of languages to accomplish this.
I did look, of course I did, but did I expect to find Uniface on that list? No I did not. They have examples for .NET, Java, PHP, Python and Ruby. So this is me, trying it the hard way, in Uniface.
I should point out, there are a few caveats to this...
- I've not done anything with encrypted tokens (JWEs).
- For signed tokens (JWSs) I've not validated the signature - I tried, but I can't get Uniface to do the encryption properly - I have a FrontLine call open about this.
- I use an included procedure "json_to_list" in a few places - this is something I'd previously written which uses string manipulation to convert a JSON string into a Uniface list.
- This code is provided as is, with no guarantee that it will work, it is merely for demonstration purposes.
entry jwt_to_list
params
string pToken : in ;JSON Web Token
(JWT)
string pList : out ;Uniface list of
data
endparams
variables
string vToken,vHeader,vHeaderJson,vHeaderList,vAlgorithm,vTokenMode,vTokenType
string vEncryption,vKeyId,vKeyUrl,vPartTwo,vPartTwoJson,vPartTwoList,vPartThree
endvariables
;check parameters
if ( pToken = "" )
return -101 ;no token
endif
;split token into
3 parts
vToken = $replace(pToken,1,".","·;",-1)
if ( $itemcount(vToken) != 3 )
return -102 ;token doesn't have 3 parts
endif
getitem vHeader,vToken,1
getitem vPartTwo,vToken,2
getitem vPartThree,vToken,3
if ( vHeader = "" | vPartTwo = "" )
return -103 ;token parts are missing (check third part later, depends on mode)
endif
;decode header
vHeaderJson = $replace($replace(vHeader,1,"_","/",-1),1,"-","+",-1)
vHeaderJson = $encode("USTRING",$decode("BASE64",vHeaderJson))
if ( $status < 0 | $procerror < 0 |
vHeaderJson = "" )
return -104 ;header could not be decoded
endif
call json_to_list(vHeaderJson,vHeaderList)
if ( vHeaderList = "" )
return -105 ;header JSON is invalid
endif
;extract header
values
getitem/id
vTokenType,vHeaderList,"typ"
delitem/id vHeaderList,"typ"
getitem/id vAlgorithm,vHeaderList,"alg"
delitem/id vHeaderList,"alg"
getitem/id vEncryption,vHeaderList,"enc"
delitem/id vHeaderList,"enc"
getitem/id vKeyId,vHeaderList,"kid"
delitem/id vHeaderList,"kid"
getitem/id vKeyUrl,vHeaderList,"jku"
delitem/id vHeaderList,"jku"
if ( vHeaderList != "" )
return -106 ;unknown header values
endif
;check signature
algorithm
selectcase ( vAlgorithm )
case "none"
vAlgorithm = "" ;plaintext token
vTokenMode = "JWT"
case "HS256"
vAlgorithm = "HMAC_SHA256" ;HMAC using SHA-256 hash
vTokenMode = "JWS"
case "HS384"
vAlgorithm = "HMAC_SHA384" ;HMAC using SHA-384 hash
vTokenMode = "JWS"
case "HS512"
vAlgorithm = "HMAC_SHA512" ;HMAC using SHA-512 hash
vTokenMode = "JWS"
case "RS256"
vAlgorithm = "RSASSA_PKCS1V15_SHA256" ;RSA SSA (PKCS) using SHA-256 hash
vTokenMode = "JWS"
case "RS384"
vAlgorithm = "RSASSA_PKCS1V15_SHA384" ;RSA SSA (PKCS) using SHA-384 hash
vTokenMode = "JWS"
case "RS512"
vAlgorithm = "RSASSA_PKCS1V15_SHA512" ;RSA SSA (PKCS) using SHA-512 hash
vTokenMode = "JWS"
case "PS256"
vAlgorithm = "RSASSA_PSS_SHA256" ;RSA SSA (PSS) using SHA-256 hash
vTokenMode = "JWS"
case "PS384"
vAlgorithm = "RSASSA_PSS_SHA384" ;RSA SSA (PSS) using SHA-384 hash
vTokenMode = "JWS"
case "PS512"
vAlgorithm = "RSASSA_PSS_SHA512" ;RSA SSA (PSS) using SHA-512 hash
vTokenMode = "JWS"
case "RAS1_5"
vAlgorithm = "RSAES_PKCS1V15" ;RSA ES (PKCS)
vTokenMode = "JWE"
case "RSA-OAEP-256"
vAlgorithm = "RSAES_OAEP_SHA256" ;RSA ES (OAEP) using SHA-256 hash
vTokenMode = "JWE"
case "ES256","ES384","ES512","RSA-OAEP","A128KW","A192KW","A256KW","dir"
return -107 ;valid, but not supported by Uniface
case "","alg"
return -108 ;no algorithm (mandatory value)
elsecase
return -109 ;unknown algorithm specified
endselectcase
;check mode/third
part
selectcase ( vTokenMode )
case "JWT"
if ( vPartThree != "" )
return -110 ;third part specified in plaintext token
endif
case "JWS"
if ( vPartThree = "" )
return -111 ;third part missing in signed token
endif
case "JWE"
if ( vPartThree = "" )
return -111 ;third part missing in encrypted token
endif
return -112 ;todo - handle this type
elsecase
return -112 ;unknown token mode
endselectcase
;decode second
part
vPartTwoJson = $replace($replace(vPartTwo,1,"_","/",-1),1,"-","+",-1)
vPartTwoJson = $encode("USTRING",$decode("BASE64",vPartTwoJson))
if ( $status < 0 | $procerror < 0 |
vPartTwoJson = "" )
return -113 ;second part could not be decoded
endif
call json_to_list(vPartTwoJson,vPartTwoList)
if ( vPartTwoList = "" )
return -114 ;second part JSON is invalid
endif
;signature mode
if ( vTokenMode = "JWS" )
;todo – validate signature
endif
;return data
if ( vTokenType = "JWS" | vTokenType = "JWE" )
call jwt_to_list(vPartTwoJson,pList) ;handle nested tokens
else
pList = vPartTwoList ;no nesting, just return data
endif
return 0
end
Summary: It is possible to handle JSON Web Tokens (JWTs), but so far I've only looked at plaintext and signed tokens, and I've not managed to validate the signature for signed tokens yet.