The type of certificate extensions you need to enforce such restriction is... Key Usage and/or Extended Key Usage extensions. For any kind of digital signature, you need at least the Key Usage called... digitalSignature, as specified in RFC 5280. Standard (Extended) Key Usage extensions are all specified in § 4.2.1.3 and 4.2.1.12 of the RFC.
You can always avoid certificates for the sake of simplicity, by maintaining a truststore (a static list) of public keys (or fingerprints if you want to optimize memory/disk usage) on the JWT verifier's side. But this has some limitations, such as:
- No standard revocation mechanism: if the signing key has been compromised, how does the verifier become aware of that? With certificates, you have the possibility to revoke certificates, and verifiers use standard OCSP or CRL to verify the revocation status.
- You have to know in advance all public keys potentially used for JWT signing. This is not always the case. (E.g. in some cases, all you want to know as a verifier is that the key belongs to some trusted organisation's entity and that it has been allowed for signing.)
- If the list of public keys is/becomes too big, it is hardly manageable.
- If the keys change too often (remember that keys should be renewed regularly), it is hardly manageable.
Therefore, if such limitations affect you, X.509 certificates offer a more scalable and flexible solution, but with an extra layer of complexity of course. With certificates, it works like this:
- Each JWT issuer has a certificate issued by one or more Certificate Authorities (CA)
- JWT verifiers should trust these CAs (list of trusted CAs), instead of trusting each JWT issuer's certificate specifically.
- JWT include the signer's certificate (or certificate chain if you use sub-CAs) in the
x5c
header parameter of the JWS header as per RFC 7515 (X.509 Certificate Chain), so that the verifier can link the certificate (chain) to one of the trusted CAs.