Crypt::JWT(3)

πŸŽ€ NAME πŸŽ€

okay so bestie, this is Crypt::JWT(3) β€” the user-contributed Perl documentation for Crypt::JWT β€” and it implements JSON Web Token (JWT, JWS, JWE) as defined by RFC7519, RFC7515, and RFC7516. πŸ’…βœ¨

✨ SYNOPSIS ✨

# encoding
use Crypt::JWT qw(encode_jwt);
my $jws_token = encode_jwt(payload=>$data, alg=>'HS256', key=>'secret');
my $jwe_token = encode_jwt(payload=>$data, alg=>'PBES2-HS256+A128KW', enc=>'A128GCM', key=>'secret');

# decoding
use Crypt::JWT qw(decode_jwt);
my $data1 = decode_jwt(token=>$jws_token, key=>'secret');
my $data2 = decode_jwt(token=>$jwe_token, key=>'secret');

🌸 DESCRIPTION 🌸

So this module implements JSON Web Token (JWT) β€” https://tools.ietf.org/html/rfc7519. And it's not just doing the signature thing, okay? The implementation covers JSON Web Signature (JWS) β€” https://tools.ietf.org/html/rfc7515 β€” and also JSON Web Encryption (JWE) β€” https://tools.ietf.org/html/rfc7516. iconic behavior, honestly. πŸ‘‘

The module implements all (100%) of the algorithms defined in https://tools.ietf.org/html/rfc7518 β€” JSON Web Algorithms (JWA). not 99%, not "most of them", literally 100%. slay. πŸ’―

This module supports Compact JWS/JWE and Flattened JWS/JWE JSON serialization. General JSON serialization is not supported yet β€” that's the one little "not yet" in the group chat, bestie. 🫢

πŸ¦‹ EXPORT πŸ¦‹

Nothing is exported by default. no surprise plot twist there. πŸ™ˆ

You can export selected functions:

use Crypt::JWT qw(decode_jwt encode_jwt);

Or all of them at once:

use Crypt::JWT ':all';

πŸ’– FUNCTIONS πŸ’–

🧚 decode_jwt

my $data = decode_jwt(%named_args);

Named arguments are the main character energy here:

πŸŽ€ token

Mandatory argument, a string with either JWS or JWE JSON Web Token. like, you literally cannot skip it, bestie. πŸ’…

### JWS token example (3 segments)
$t = "eyJhbGciOiJIUzI1NiJ9.dGVzdA.ujBihtLSr66CEWqN74SpLUkv28lra_CeHnxLmLNp4Jo";
my $data = decode_jwt(token=>$t, key=>$k);

### JWE token example (5 segments)
$t = "eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTEyOEtXIn0.UusxEbzhGkORxTRq0xkFKhvzPrXb9smw.VGfOuq0Fxt6TsdqLZUpnxw.JajIQQ.pkKZ7MHS0XjyGmRsqgom6w";
my $data = decode_jwt(token=>$t, key=>$k);

πŸ”‘ key

A key used for token decryption (JWE) or token signature validation (JWS). The value depends on the alg token header value. and there are so many possibilities, so here's the whole key menu, no cap:

JWS alg header      key value
------------------  ----------------------------------
none                no key required
HS256               string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct')
HS384               dtto
HS512               dtto
RS256               public RSA key, perl HASH ref with JWK key structure,
                    a reference to SCALAR string with PEM or DER or JSON/JWK data,
                    object: Crypt::PK::RSA, Crypt::OpenSSL::RSA, Crypt::X509 or Crypt::OpenSSL::X509
RS384               public RSA key, see RS256
RS512               public RSA key, see RS256
PS256               public RSA key, see RS256
PS384               public RSA key, see RS256
PS512               public RSA key, see RS256
ES256               public ECC key, perl HASH ref with JWK key structure,
                    a reference to SCALAR string with PEM or DER or JSON/JWK data,
                    an instance of Crypt::PK::ECC
ES256K              public ECC key, see ES256
ES384               public ECC key, see ES256
ES512               public ECC key, see ES256
EdDSA               public Ed25519 key

JWE alg header      key value
------------------  ----------------------------------
dir                 string (raw octects) or perl HASH ref with JWK, kty=>'oct', length depends on 'enc' algorithm
A128KW              string (raw octects) 16 bytes (or perl HASH ref with JWK, kty=>'oct')
A192KW              string (raw octects) 24 bytes (or perl HASH ref with JWK, kty=>'oct')
A256KW              string (raw octects) 32 bytes (or perl HASH ref with JWK, kty=>'oct')
A128GCMKW           string (raw octects) 16 bytes (or perl HASH ref with JWK, kty=>'oct')
A192GCMKW           string (raw octects) 24 bytes (or perl HASH ref with JWK, kty=>'oct')
A256GCMKW           string (raw octects) 32 bytes (or perl HASH ref with JWK, kty=>'oct')
PBES2-HS256+A128KW  string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct')
PBES2-HS384+A192KW  string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct')
PBES2-HS512+A256KW  string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct')
RSA-OAEP            private RSA key, perl HASH ref with JWK key structure,
                    a reference to SCALAR string with PEM or DER or JSON/JWK data,
                    an instance of Crypt::PK::RSA or Crypt::OpenSSL::RSA
RSA-OAEP-256        private RSA key, see RSA-OAEP
RSA1_5              private RSA key, see RSA-OAEP
ECDH-ES             private ECC or X25519 key, perl HASH ref with JWK key structure,
                    a reference to SCALAR string with PEM or DER or JSON/JWK data,
                    an instance of Crypt::PK::ECC
ECDH-ES+A128KW      private ECC or X25519 key, see ECDH-ES
ECDH-ES+A192KW      private ECC or X25519 key, see ECDH-ES
ECDH-ES+A256KW      private ECC or X25519 key, see ECDH-ES

Example using the key from the jwk token header:

my $data = decode_jwt(token=>$t, key_from_jwk_header=>1);
my ($header, $data) = decode_jwt(token=>$t, decode_header=>1, key_from_jwk_header=>1);

Examples with raw octet keys:

#string
my $data = decode_jwt(token=>$t, key=>'secretkey');
#binary key
my $data = decode_jwt(token=>$t, key=>pack("H*", "788A6E38F36B7596EF6A669E94"));
#perl HASH ref with JWK structure (key type 'oct')
my $data = decode_jwt(token=>$t, key=>{kty=>'oct', k=>"GawgguFyGrWKav7AX4VKUg"});

Examples with RSA keys:

my $pem_key_string = <<'EOF';
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCoVm/Sl5r+Ofky
jioRSZK26GW6WyjyfWKddsSi13/NOtCn0rRErSF/u3QrgGMpWFqKohqbi1VVC+SZ
...
8c1vm2YFafgdkSk9Qd1oU2Fv1aOQy4VovOFzJ3CcR+2r7cbRfcpLGnintHtp9yek
02p+d5g4OChfFNDhDtnIqjvY
-----END PRIVATE KEY-----
EOF

my $jwk_key_json_string = '{"kty":"RSA","n":"0vx7agoebG...L6tSoc_BJECP","e":"AQAB"}';

#a reference to SCALAR string with PEM or DER or JSON/JWK data,
my $data = decode_jwt(token=>$t, key=>\$pem_key_string);
my $data = decode_jwt(token=>$t, key=>\$der_key_string);
my $data = decode_jwt(token=>$t, key=>\$jwk_key_json_string);

#instance of Crypt::PK::RSA
my $data = decode_jwt(token=>$t, key=>Crypt::PK::RSA->new('keyfile.pem'));
my $data = decode_jwt(token=>$t, key=>Crypt::PK::RSA->new(\$pem_key_string));

#instance of Crypt::OpenSSL::RSA
my $data = decode_jwt(token=>$t, key=>Crypt::OpenSSL::RSA->new_private_key($pem_key_string));

#instance of Crypt::X509 (public key only)
my $data = decode_jwt(token=>$t, key=>Crypt::X509->new(cert=>$cert));

#instance of Crypt::OpenSSL::X509 (public key only)
my $data = decode_jwt(token=>$t, key=>Crypt::OpenSSL::X509->new_from_file('cert.pem'));
my $data = decode_jwt(token=>$t, key=>Crypt::OpenSSL::X509->new_from_string($cert));

#perl HASH ref with JWK structure (key type 'RSA')
my $rsa_priv = {
  kty => "RSA",
  n   => "0vx7agoebGcQSuuPiLJXZpt...eZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
  e   => "AQAB",
  d   => "X4cTteJY_gn4FYPsXB8rdXi...FLN5EEaG6RoVH-HLKD9Mdx5ooGURknhnrRwUkC7h5fJLMWbFAKLWY2v7B6NqSzUvx0_YSf",
  p   => "83i-7IvMGXoMXCskv73TKr8...Z27zvoj6pbUQyLPBQxtPnwD20-60eTmD2ujMt5PoMrm8RmNhVWtjjMmMjOpSicFHjXOuVI",
  q   => "3dfOR9cuYq-0S-mkFLzgItg...q3hWeMuG0ouqnb3obLyuqjVZQ1dIrdgTnCdYzBcOW5r37AFXjift_NGiovonzhKpoVVS78",
  dp  => "G4sPXkc6Ya9y8oJW9_ILj4...zi_H7TkS8x5SdX3oE0oiYwxIiemTAu0UOa5pgFGyJ4c8t2VF40XRugKTP8akhFo5tA77Qe",
  dq  => "s9lAH9fggBsoFR8Oac2R_E...T2kGOhvIllTE1efA6huUvMfBcpn8lqW6vzzYY5SSF7pMd_agI3G8IbpBUb0JiraRNUfLhc",
  qi  => "GyM_p6JrXySiz1toFgKbWV...4ypu9bMWx3QJBfm0FoYzUIZEVEcOqwmRN81oDAaaBk0KWGDjJHDdDmFW3AN7I-pux_mHZG",
};
my $data = decode_jwt(token=>$t, key=>$rsa_priv});

Examples with ECC keys:

my $pem_key_string = <<'EOF';
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBG1c3z52T8XwMsahGVdOZWgKCQJfv+l7djuJjgetdbDoAoGCCqGSM49
AwEHoUQDQgAEoBUyo8CQAFPeYPvv78ylh5MwFZjTCLQeb042TjiMJxG+9DLFmRSM
lBQ9T/RsLLc+PmpB1+7yPAR+oR5gZn3kJQ==
-----END EC PRIVATE KEY-----
EOF

my $jwk_key_json_string = '{"kty":"EC","crv":"P-256","x":"MKB..7D4","y":"4Et..FyM"}';

#a reference to SCALAR string with PEM or DER or JSON/JWK data,
my $data = decode_jwt(token=>$t, key=>\$pem_key_string);
my $data = decode_jwt(token=>$t, key=>\$der_key_string);
my $data = decode_jwt(token=>$t, key=>\$jwk_key_json_string);

#instance of Crypt::PK::ECC
my $data = decode_jwt(token=>$t, key=>Crypt::PK::ECC->new('keyfile.pem'));
my $data = decode_jwt(token=>$t, key=>Crypt::PK::ECC->new(\$pem_key_string));

#perl HASH ref with JWK structure (key type 'EC')
my $ecc_priv = {
  kty => "EC",
  crv => "P-256",
  x   => "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
  y   => "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
  d   => "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE",
};
my $data = decode_jwt(token=>$t, key=>$ecc_priv});

πŸ” keypass

When the key parameter is an encrypted private RSA or ECC key, this optional parameter may contain a password for private key decryption. basically the little password sidekick for your encrypted key. πŸ—οΈ

πŸ—‚οΈ kid_keys

This parameter can be either a JWK Set JSON string (see RFC7517) or a perl HASH ref with JWK Set structure like this:

my $keylist = {
  keys => [
    { kid=>"key1", kty=>"oct", k=>"GawgguFyGrWKav7AX4VKUg" },
    { kid=>"key2", kty=>"oct", k=>"ulxLGy4XqhbpkR5ObGh1gX" },
  ]
};
my $payload = decode_jwt(token=>$t, kid_keys=>$keylist);

You can use export_key_jwk in Crypt::PK::RSA to generate a JWK for RSA:

my $pubkey = Crypt::PK::RSA->new('rs256-4096-public.pem');
my $jwk_hash = $pubkey->export_key_jwk('public', 1);
$jwk_hash->{kid} = 'key1';
my $keylist = {
  keys => [
    $jwk_hash,
  ]
};

The structure described above is used e.g. by https://www.googleapis.com/oauth2/v2/certs

use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new;
my $google_keys => $ua->get('https://www.googleapis.com/oauth2/v2/certs')->result->json;
my $payload = decode_jwt(token => $t, kid_keys => $google_keys);

SINCE 0.019 we also support the alternative structure used e.g. by https://www.googleapis.com/oauth2/v1/certs:

use LWP::Simple;
my $google_certs = get('https://www.googleapis.com/oauth2/v1/certs');
my $payload = decode_jwt(token => $t, kid_keys => $google_certs);

ok so ✨ the moment your token header has a kid item in it, bestie, the matching key gets looked up in the kid_keys list and used to decode the token β€” so like, you do not need to hand over an explicit key through the key parameter at all πŸ’…. If you're making a token and want a kid header in there, just add one via extra_headers. iykyk.

🚨 INCOMPATIBLE CHANGE in 0.023: when kid_keys is specified, it croaks if the token header has no kid value, or if the kid isn't found in kid_keys. no vibes-passing here, it just dies 😭

  • key_from_jwk_header β€” SINCE 0.023. this one decides whether we trust the jwk header value to validate a JWS signature πŸ¦‹
    • 1 β€” use the jwk header value for validating the JWS signature if neither key nor kid_keys was specified. BEWARE: DANGEROUS, UNSECURE!!! πŸ’€
    • 0 (default) β€” ignore the jwk header value when validating the JWS signature. the safe, boring, iconic choice πŸ‘‘

keep in mind that turning on key_from_jwk_header requires the jwk header to actually exist and to be a valid RSA/ECDSA public key β€” otherwise it croaks 🫠

  • allow_none β€” the "do we accept unsigned tokens" switch πŸ™ˆ
    • 1 β€” accept JWS tokens whose alg header value is none (which literally means the token has no signature at all). BEWARE: DANGEROUS, UNSECURE!!!
    • 0 (default) β€” do not allow JWS with a none alg header value. slay, keep it that way πŸŽ€
  • ignore_signature β€” exactly what it sounds like, bestie 😭
    • 1 β€” do not check the signature on JWS tokens. BEWARE: DANGEROUS, UNSECURE!!!
    • 0 (default) β€” check the signature on JWS tokens βœ…
  • accepted_alg β€” your guest list for alg algorithms πŸ‘‘
    • undef (default) β€” accept all alg algorithms except none (and if you truly want none you use allow_none)
    • string β€” name of the accepted alg algorithm (only one, one single algorithm, just the one πŸ’…)
    • ARRAY ref β€” a list of accepted alg algorithms
    • Regexp β€” has to match the alg algorithm name
my $payload = decode_jwt(token=>$t, key=>$k, accepted_alg=>'HS256');
#or
my $payload = decode_jwt(token=>$t, key=>$k, accepted_alg=>['HS256','HS384']);
#or
my $payload = decode_jwt(token=>$t, key=>$k, accepted_alg=>qr/^HS(256|384|512)$/);
  • accepted_enc β€” same energy, but for enc algorithms 🌸
    • undef (default) β€” accept all enc algorithms
    • string β€” name of the accepted enc algorithm (only one)
    • ARRAY ref β€” a list of accepted enc algorithms
    • Regexp β€” has to match the enc algorithm name
my $payload = decode_jwt(token=>$t, key=>$k, accepted_enc=>'A192GCM');
#or
my $payload = decode_jwt(token=>$t, key=>$k, accepted_enc=>['A192GCM','A256GCM']);
#or
my $payload = decode_jwt(token=>$t, key=>$k, accepted_enc=>qr/^A(128|192|256)GCM$/);
  • decode_payload β€” how much do we decode the payload, hmm? πŸ¦‹
    • 0 β€” do not decode the payload, just return it as a raw string (octets)
    • 1 β€” decode the payload from a JSON string and return it as a perl hash ref (or array ref) β€” a decode_json failure here means a fatal error (croak) πŸ’€
    • undef (default) β€” if possible, decode the payload from a JSON string; if decode_json fails, return the payload as a raw string (octets). the flexible bestie option ✨
  • decode_header β€” do you want the header back too? πŸŽ€
    • 0 (default) β€” do not return the decoded header as a return value of decode_jwt()
    • 1 β€” return the decoded header as a return value of decode_jwt()
my $payload = decode_jwt(token=>$t, key=>$k);
#or
my ($header, $payload) = decode_jwt(token=>$t, key=>$k, decode_header=>1);
  • verify_iss β€” checking the iss (Issuer) claim πŸ‘‘
🚨 INCOMPATIBLE CHANGE in 0.024: if verify_iss is specified and the claim iss (Issuer) is completely missing, that's a failure since 0.024. no claim, no mercy 😭
  • CODE ref β€” a subroutine (with the iss claim value passed as its argument) that should return true, otherwise verification fails
  • Regexp ref β€” the iss claim value has to match the given regexp, otherwise verification fails
  • Scalar β€” the iss claim value has to be equal to the given string (since 0.029)
  • undef (default) β€” do not verify the iss claim
  • verify_aud β€” checking the aud (Audience) claim 🫢
🚨 INCOMPATIBLE CHANGE in 0.024: if verify_aud is specified and the claim aud (Audience) is completely missing, that's a failure since 0.024.
  • CODE ref β€” a subroutine (with the aud claim value passed as its argument) that should return true, otherwise verification fails
  • Regexp ref β€” the aud claim value has to match the given regexp, otherwise verification fails
  • Scalar β€” the aud claim value has to be equal to the given string (since 0.029)
  • undef (default) β€” do not verify the aud claim

✨ SINCE 0.036 we handle the aud claim when it contains an array of strings. in that case the check should succeed if at least one value from the array matches, bestie 🌈. all the checks (CODE, Regexp, Scalar) are performed individually against each member of the array of strings.

  • verify_sub β€” checking the sub (Subject) claim πŸ’œ
🚨 INCOMPATIBLE CHANGE in 0.024: if verify_sub is specified and the claim sub (Subject) is completely missing, that's a failure since 0.024.
  • CODE ref β€” a subroutine (with the sub claim value passed as its argument) that should return true, otherwise verification fails
  • Regexp ref β€” the sub claim value has to match the given regexp, otherwise verification fails
  • Scalar β€” the sub claim value has to be equal to the given string (since 0.029)
  • undef (default) β€” do not verify the sub claim
  • verify_jti β€” checking the jti (JWT ID) claim πŸ“
🚨 INCOMPATIBLE CHANGE in 0.024: if verify_jti is specified and the claim jti (JWT ID) is completely missing, that's a failure since 0.024.
  • CODE ref β€” a subroutine (with the jti claim value passed as its argument) that should return true, otherwise verification fails
  • Regexp ref β€” the jti claim value has to match the given regexp, otherwise verification fails
  • Scalar β€” the jti claim value has to be equal to the given string (since 0.029)
  • undef (default) β€” do not verify the jti claim
  • verify_iat β€” Issued At claim behaviour, no cap ⏰
    • undef β€” the Issued At iat claim must be valid (not in the future) if present
    • 0 (default) β€” ignore the iat claim
    • 1 β€” require a valid iat claim
  • verify_nbf β€” Not Before claim behaviour 🌸
    • undef (default) β€” the Not Before nbf claim must be valid if present
    • 0 β€” ignore the nbf claim
    • 1 β€” require a valid nbf claim
  • verify_exp β€” Expiration Time claim behaviour βŒ›
    • undef (default) β€” the Expiration Time exp claim must be valid if present
    • 0 β€” ignore the exp claim
    • 1 β€” require a valid exp claim
  • leeway β€” tolerance in seconds, related to verify_exp, verify_nbf and verify_iat. default is 0. a lil grace period, a lil wiggle room 🧚
  • ignore_claims β€” the "trust me bro" switch 😭
    • 1 β€” do not check claims (iat, exp, nbf, iss, aud, sub, jti). BEWARE: DANGEROUS, UNSECURE!!!
    • 0 (default) β€” check claims βœ…
  • verify_typ β€” SINCE 0.036, checking the typ header parameter πŸ’…
    • CODE ref β€” a subroutine (with the typ header parameter value passed as its argument) that should return true, otherwise verification fails
    • Regexp ref β€” the typ header parameter value has to match the given regexp, otherwise verification fails
    • Scalar β€” the typ header parameter value has to be equal to the given string
    • undef (default) β€” do not verify the typ header parameter
  • tolerate_padding β€” Base64 padding opinions πŸŽ€
    • 0 (default) β€” ignore Base64 padding characters when validating the signature
    • 1 β€” take account of Base64 padding characters when validating the signature

✨ encode_jwt ✨

my $token = encode_jwt(%named_args);

here's the named arguments, bestie 🫢

payload

the value of this mandatory parameter can be a string/buffer, or a HASH ref, or an ARRAY ref 🌈

my $token = encode_jwt(payload=>"any raw data", key=>$k, alg=>'HS256');
#or
my $token = encode_jwt(payload=>{a=>1,b=>2}, key=>$k, alg=>'HS256');
#or
my $token = encode_jwt(payload=>[11,22,33,44], key=>$k, alg=>'HS256');

HASH refs and ARRAY refs payloads get serialized as JSON strings, periodt ✨

alg

the alg header value is mandatory for both JWE and JWS tokens. no alg, no token, sorry 😭

supported JWE alg algorithms πŸ‘‘

dir
A128KW
A192KW
A256KW
A128GCMKW
A192GCMKW
A256GCMKW
PBES2-HS256+A128KW
PBES2-HS384+A192KW
PBES2-HS512+A256KW
RSA-OAEP
RSA-OAEP-256
RSA1_5
ECDH-ES+A128KW
ECDH-ES+A192KW
ECDH-ES+A256KW
ECDH-ES

supported JWS algorithms πŸ’œ

none   ...  no integrity (NOTE: disabled by default)
HS256  ...  HMAC+SHA256 integrity
HS384  ...  HMAC+SHA384 integrity
HS512  ...  HMAC+SHA512 integrity
RS256  ...  RSA+PKCS1-V1_5 + SHA256 signature
RS384  ...  RSA+PKCS1-V1_5 + SHA384 signature
RS512  ...  RSA+PKCS1-V1_5 + SHA512 signature
PS256  ...  RSA+PSS + SHA256 signature
PS384  ...  RSA+PSS + SHA384 signature
PS512  ...  RSA+PSS + SHA512 signature
ES256  ...  ECDSA + SHA256 signature
ES256K ...  ECDSA + SHA256 signature
ES384  ...  ECDSA + SHA384 signature
ES512  ...  ECDSA + SHA512 signature
EdDSA  ...  Ed25519 signature

enc

the enc header is mandatory for JWE tokens. main character rule πŸŽ€

supported enc algorithms 🌸

A128GCM
A192GCM
A256GCM
A128CBC-HS256
A192CBC-HS384
A256CBC-HS512

key

a key used for token encryption (JWE) or token signing (JWS) πŸ”‘. the value you pass depends on the alg token header value, so like… match the vibe to the alg, okay?

  • none β€” no key required
  • HS256 β€” string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct')
  • HS384 β€” dtto
  • HS512 β€” dtto
  • RS256 β€” private RSA key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, object: Crypt::PK::RSA, Crypt::OpenSSL::RSA, Crypt::X509 or Crypt::OpenSSL::X509
  • RS384 β€” private RSA key, see RS256
  • RS512 β€” private RSA key, see RS256
  • PS256 β€” private RSA key, see RS256
  • PS384 β€” private RSA key, see RS256
  • PS512 β€” private RSA key, see RS256
  • ES256 β€” private ECC key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::ECC
  • ES256K β€” private ECC key, see ES256
  • ES384 β€” private ECC key, see ES256
  • ES512 β€” private ECC key, see ES256
  • EdDSA β€” private Ed25519 key

okay so when you're doing JWE, the alg header is literally the vibe check for your key β€” the JWE alg header β†’ key value mapping is:

  • dir β€” string (raw octects) or perl HASH ref with JWK, kty=>'oct', length depends on 'enc' algorithm
  • A128KW β€” string (raw octects) 16 bytes (or perl HASH ref with JWK, kty=>'oct')
  • A192KW β€” string (raw octects) 24 bytes (or perl HASH ref with JWK, kty=>'oct')
  • A256KW β€” string (raw octects) 32 bytes (or perl HASH ref with JWK, kty=>'oct')
  • A128GCMKW β€” string (raw octects) 16 bytes (or perl HASH ref with JWK, kty=>'oct')
  • A192GCMKW β€” string (raw octects) 24 bytes (or perl HASH ref with JWK, kty=>'oct')
  • A256GCMKW β€” string (raw octects) 32 bytes (or perl HASH ref with JWK, kty=>'oct')
  • PBES2-HS256+A128KW β€” string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct')
  • PBES2-HS384+A192KW β€” string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct')
  • PBES2-HS512+A256KW β€” string (raw octects) of any length (or perl HASH ref with JWK, kty=>'oct')
  • RSA-OAEP β€” public RSA key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::RSA or Crypt::OpenSSL::RSA
  • RSA-OAEP-256 β€” public RSA key, see RSA-OAEP
  • RSA1_5 β€” public RSA key, see RSA-OAEP
  • ECDH-ES β€” public ECC or X25519 key, perl HASH ref with JWK key structure, a reference to SCALAR string with PEM or DER or JSON/JWK data, an instance of Crypt::PK::ECC
  • ECDH-ES+A128KW β€” public ECC or X25519 key, see ECDH-ES
  • ECDH-ES+A192KW β€” public ECC or X25519 key, see ECDH-ES
  • ECDH-ES+A256KW β€” public ECC or X25519 key, see ECDH-ES
  • keypass β€” when the key parameter is an encrypted private RSA or ECC key, this optional parameter may contain a password for private key decryption.
  • allow_none β€” bestie, read this twice:
    • 1 - allow JWS with "none" 'alg' header value (which means that token has no signature), BEWARE: DANGEROUS, UNSECURE!!!
    • 0 (default) - do not allow JWS with "none" 'alg' header value
  • extra_headers β€” optional HASH ref with items that will be added to JWT header.

If you want to use PBES2-based 'alg' like "PBES2-HS512+A256KW" you can set PBES2 salt len (p2s) in bytes and iteration count (p2c) via extra_headers like this:

my $token = encode_jwt(payload=>$p, key=>$k, alg=>'PBES2-HS512+A256KW', extra_headers=>{p2c=8000, p2s=>32});
#NOTE: handling of p2s header is a special case, in the end it is replaced with the generated salt

You can also use this to specify a kid value (see "kid_keys")

my $token = encode_jwt(payload=>$p, key=>$k, alg => 'RS256', extra_headers=>{kid=>'key1'});
  • unprotected_headers β€” a hash with additional integrity unprotected headers - JWS and JWE (not available for "compact" serialization);
  • shared_unprotected_headers β€” a hash with additional integrity unprotected headers - JWE only (not available for "compact" serialization);
  • aad β€” Additional Authenticated Data - scalar value with any (even raw octects) data - JWE only (not available for "compact" serialization);
  • serialization β€” specify serialization method: "compat" (= default) for Compact JWS/JWE serialization or "flattened" for Flattened JWS/JWE JSON serialization.

General JSON serialization is not supported yet.

  • zip β€” compression method, currently 'deflate' is the only one supported. "undef" (default) means no compression.
my $token = encode_jwt(payload=>$p, key=>$k, alg=>'HS256', zip=>'deflate');
#or define compression level
my $token = encode_jwt(payload=>$p, key=>$k, alg=>'HS256', zip=>['deflate', 9]);
  • auto_iat β€” 1 = set 'iat' (Issued At) claim to current time (epoch seconds since 1970) at the moment of token encoding. 0 (default) = do not set 'iat' claim.
NOTE: claims are part of the payload and can be used only if the payload is a HASH ref!
  • relative_exp β€” set 'exp' claim (Expiration Time) to current time + relative_exp value (in seconds).
NOTE: claims are part of the payload and can be used only if the payload is a HASH ref!
  • relative_nbf β€” set 'nbf' claim (Not Before) to current time + relative_nbf value (in seconds).
NOTE: claims are part of the payload and can be used only if the payload is a HASH ref!

✨ SEE ALSO ✨

go give these icons a little love too, bestie:

  • Crypt::Cipher::AES
  • Crypt::AuthEnc::GCM
  • Crypt::PK::RSA
  • Crypt::PK::ECC
  • Crypt::KeyDerivation
  • Crypt::KeyWrap

πŸ“œ LICENSE πŸ“œ

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

Copyright (c) 2015-2025 DCIT, a.s. <https://www.dcit.cz> / Karel Miko


that's the whole tea, bestie β€” source/version is perl v5.42.0, the date is 2025-07-25, and this page is genuinely giving iconic documentation energy. mwah πŸ’–πŸ¦‹βœ¨

πŸ“¦ where this page comes from