OW3.$verifyjwt()

Function group Execute on client Platform(s)
OW3 NO All

Syntax

OW3.$verifyjwt(xJWT,vPublicKey[,&rClaims,&cErrorText])

Description

Returns kTrue if the JWT signature is valid and the current time is within its exp and nbf claims, and returns the payload in rClaims. It does not check the iss or aud claims; check those in rClaims.

$verifyjwt() checks that a JSON Web Token (JWT) was signed by the holder of a key you trust, and that it is within its validity period. It returns kTrue only when the signature is valid and the current time is within the token's exp and nbf claims. Use $parsejwt() instead if you only want to read a token without verifying it.

$verifyjwt() does not check the iss, aud or any other claim. Check those yourself in rClaims after a successful call. This matters when the key belongs to an authorization server that issues tokens for other applications as well as yours: such a token has a valid signature, but it was not meant for you.

The parameters are as follows:

Parameter Description
xJWT The JWT in compact serialization: three base64url parts separated by dots. Either character, or binary containing UTF-8. Pass the token alone, without any "Bearer " prefix or surrounding white space
vPublicKey The key or keys to verify with. Either a PEM public key (text starting "-----BEGIN PUBLIC KEY-----" or "-----BEGIN RSA PUBLIC KEY-----"), a single JWK, or a JWKS (a JSON object with a keys array, as served from an authorization server's jwks_uri). A JWK or JWKS can be passed as JSON text, as binary containing UTF-8 JSON, or as a row that you have already parsed from the JSON
rClaims Optional. Receives the token's payload as a row, with one column per claim, when the token is valid. It is set to an empty row whenever the call fails, so a failed call never leaves the claims of an earlier token behind
cErrorText Optional. Receives an error code when the call fails (see below), and is empty when it succeeds

Algorithms and keys

The token's alg header must be one of RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 or ES512. Tokens that use HMAC (HS256 and so on), or that are unsigned (alg "none"), are always refused. This means a public key can never be used as an HMAC secret, and an attacker cannot remove the signature from a token.

RSA keys must be at least 2048 bits. For the ES algorithms, the key must be on the curve the algorithm names: P-256 for ES256, P-384 for ES384 and P-521 for ES512.

When vPublicKey is a JWK or JWKS, each key is tried only if all of the following are true:

The token is valid if any key that is tried verifies its signature. When vPublicKey is a PEM key, the token's kid is ignored.

Expiry

The exp (expiry) and nbf (not before) claims are checked only when the payload contains them. A token with neither claim never expires. Both checks allow 60 seconds of leeway, to allow for a difference between the clocks of the issuer and the machine running Omnis. A token is therefore accepted for up to 60 seconds after its exp time, and from 60 seconds before its nbf time.

Error codes

cErrorText starts with one of the codes below. Some codes are followed by ": " and more detail, for example "unsupported_alg: HS256". To test for a code, compare against the text before the first ": " rather than the whole value.

Code Meaning
invalid_request xJWT or vPublicKey was not passed
malformed_jwt The token cannot be read. It is empty or not character or binary, does not have three parts, a part is not valid base64url, the header has no alg, the header or payload is not a JSON object, or exp or nbf is not a number. The detail says which
unsupported_alg The token's algorithm is not one of those listed above. The detail names it
unsupported_crit The token header has a crit member. Tokens that use critical header extensions are refused, as RFC 7515 requires when they are not understood
invalid_keys vPublicKey cannot be read: it is not character, binary or a row, is neither a PEM key nor JSON, the JSON is not an object, or keys is not an array of objects. The detail says which
invalid_key A key was chosen but could not be used: for example, a JWK is missing a member, a PEM key is of the wrong type for the algorithm or on the wrong curve, or an RSA key is shorter than 2048 bits
unknown_kid The token has a kid, and the JWKS has no key with that kid and no key without a kid that could be tried. This usually means the authorization server has started signing with a new key: fetch its JWKS again and repeat the call
no_matching_key No key was suitable for the token's algorithm (because of its kty, use or alg), or vPublicKey is empty text or a null row
invalid_signature The signature does not match. The token was altered, or was signed with a different key
expired The token's exp time has passed, allowing for the leeway
not_yet_valid The token's nbf time has not yet been reached, allowing for the leeway
internal_error The token could not be hashed. This should not happen

Example

The following example verifies an access token against a JWKS fetched earlier from the authorization server, and then checks the issuer:

Do OW3.$verifyjwt(lToken,iJWKS,lClaims,lErrorText) Returns lValid
If not(lValid)
  Calculate lErrorCode as strtok("lErrorText",":")  ## Collect up to first ":"
  If lErrorCode="unknown_kid"
    # The server has a new signing key: fetch the JWKS into iJWKS again, then retry once
  End If
  Quit method kFalse
End If
If lClaims.iss<>"https://auth.example.com"
  Quit method kFalse
End If