1# Tink's JWT HOW-TO 2 3This is a short introduction on the JSON Web Token (JWT) library in 4[Tink](https://github.com/google/tink). 5 6The JWT standard is defined in 7[RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519). The standard has many 8options, and many options are rarely used and some options are difficult to use 9correctly. The goal of Tink's JWT implementation is provide as subset of the JWT 10standard that we consider safe to use, and fit well into Tink. 11 12These are the limitations of Tink's JWT library: 13 14- Tink only supports the 15 [JWS Compact Serialization](https://datatracker.ietf.org/doc/html/rfc7515#section-3.1) 16 format. 17 [JWS JSON Serialization](https://datatracker.ietf.org/doc/html/rfc7515#section-3.2) 18 and [JWE](https://datatracker.ietf.org/doc/html/rfc7516) is not supported. 19- Tink does not support the `None` value in the `alg` header. 20- Tink only supports the headers `typ`, `alg` and `kid`. All other headers are 21 not supported. 22- Tink does not allow tokens to be parsed before the signature/mac is 23 verified. 24 25## JWT Primitives and Key Types 26 27Tink's JWT library provides primitives for both MAC and signatures. The 28interfaces for these primitives are called `JwtMac`, `JwtPublicKeySign` and 29`JwtPublicKeyVerify`. As with normal MACs and signatures, symmetric and 30asymmetric primitives are kept separate. 31 32Note that even though the JWT library uses the same MAC and signature algorithms 33as the normal MAC and signature primitives, the JWT library 34uses *different* key types. This is needed because some metadata (such as `alg` 35and `kid`) needs to be stored with the key. 36 37If tokens are generated and verified by different entities, then you should use 38asymmetric keys with the primitives `JwtPublicKeySign` and `JwtPublicKeyVerify`. 39The private key is used to generate tokens, and the public key is used to 40verify tokens. The algorithms supported by these primitives are: `ES256`, 41`ES384`, `ES512`, `RS256`, `RS384`, `RS512`, `PS256`, `PS384` and `PS512`. Only 42use the `JwtMac` primitive if the tokens are generated and verified by the same 43entity. The algorithms supported by this primitive are `HS256`, `HS384` and 44`HS512`. 45 46In [Tinkey](TINKEY.md), all templates that can be used with the JWT primitives 47start with `JWT_`, followed by the algorithm and some additonal paramters. For 48example `JWT_HS256`, `JWT_ES256`, `JWT_RS256_3072_F4` or `JWT_PS256_3072_F4`. 49 50## Public keyset distribution 51 52In most cases, JWTs are generated by one party using a private keyset and 53verified by another party using the public keyset. This requires that the public 54keyset can be exported and shared. Tink allows public keyset to be converted to 55and from the standard 56[JWK Set](https://datatracker.ietf.org/doc/html/rfc7517#section-5) format, which 57most JWT libraries will understand. (Note that while Tink's `JsonKeysetWriter` 58will also produce keysets in JSON format, the output is *not* a JWK Set and 59other libraries will not be able to read it.) 60 61Tink does not support exporting public JWT keysets in any other format. The 62reason for this is that other formats do not contain the `alg` and the `kid` 63metadata to be used in the verification, which makes using them more error-prone 64and may make it more difficult to rotate keys. 65 66It is preferable to not just share the public keyset once, but to provide a way 67to *automatically* update the public keyset. (If not, rotating to a new key will 68be very hard.) Often, this is done by publishing the public keyset on a trusted 69and secured URL. A server that verifies tokens then simply has to periodically 70re-fetch the public keyset from that URL, for example once per day. To rotate 71the key, the new public key needs to be added to the public keyset *at least one 72day before* it is used to sign tokens. Otherwise the new tokens signed with the 73new private key will be rejected by servers that still use the old public keyset. 74 75Some implementations re-fetch the public key as soon as a token with an unknown 76`kid` header is received. Implementing this logic is not possible in Tink, since 77we don't parse the token before they are verified. We also don't recommend doing 78this, as it may result in lots of extra requests to the keyset URL if malicious 79tokens with random `kid` headers are sent. 80 81## Headers and Claims 82 83#### Algorithm Header `alg` 84 85The `alg` header is always set and verified automatically by the Tink primitives 86and cannot be accessed by the user. This makes it easier to rotate to a 87different key type, as the user code does not depend on the particular key type 88used. 89 90#### Key ID Header `kid` 91 92The `kid` header is set and verified automatically by the Tink primitives. Tink 93supports both tokens with and without a `kid` header. They can be used in 94exactly the same way, both support key rotation. Tokens without a `kid` header 95are a bit shorter, but the difference is small. It is usually safer to generate 96tokens with the `kid` header set, as other JWT libraries may require them to be 97set. 98 99Tinkey JWT key templates without a `_RAW` suffix (such as `JWT_ES256`) generate 100tokens with `kid` header. Templates with `_RAW` suffix (such as `JWT_ES256_RAW`) 101generate tokens without `kid` header. 102 103#### Type Header `typ` 104 105The goal of the `typ` header is to clearly separate different types of token 106from each other. Not doing this separation correctly may result in security 107issues. 108 109But there are other ways to do this separation, for example by distinguishing 110different tokens by the claim names they use, or simply by using different 111keyset for different tokens. We therefore don't enforce the usage of the `typ` 112header. But, when a token with a `typ` header is verified, we require that the 113verifier explicitly validates it, or explicitly ignores it. This makes sure that 114the validation of this header is not forgotten. 115 116Setting the `typ` header always to the constant value `JWT` is not needed. 117 118#### Issuer claim `iss` 119 120This claim identifies the party that signed the token. This claim is usually a 121constant value for a given keyset, and globally unique. Often a URI is used to 122make it unique. Often, identity of the issuer is already clear by the fact that 123only the owner of the private keyset (which is the issuer) can generate tokens. 124In these cases, it may not be necessary to set the issuer claim in the token. 125But if the issuer claim is set, then we require that the verifier explicitly 126checks it, or explicitly ignores it. 127 128Tink does not support to dynamically look-up a keyset based on the issuer claim, 129as this may lead to security problems if the lookup is done without proper 130validation. We require that the verifier know the issuer and the keyset used to 131verify the token before looking at the token. 132 133#### Audiences claim `aud` 134 135This claim identifies a list of verifier that may accept this token. If this 136claim is set the verifier is required to pass its identity as expected 137audience. We do allow to explicitly ignore this claim, as it may be needed in 138some cases, for example if verifier's identity changed, and both the old 139and the new identity need to be accepted. 140 141#### Expiration `exp`, not-before `nbf` and issued-at `iat` claims 142 143In most use cases, tokens should have an expiration date set. That's why Tink 144by default expects the user to set an expiration. There are some tokens that 145don't have expiration dates, see for example 146[RFC 8417](https://datatracker.ietf.org/doc/html/rfc8417). Tink allows this, 147but requires the user to explicitly pass a parameter without_expiration when 148creating a token, to make sure that this is on purpose. 149 150* The expiration date claim `exp` is *always* validated. Tink does not allow 151 to turn this off, because the content of an expired token could be outdated 152 or invalid, and should not be used. 153 154* The not-before claim `nbf` is rarly used, but it is supported and will 155 always be validated. 156 157* Tink optionally supports validating that the issued-at claim `iat` is not in 158 the pass. 159 160#### Validating any other claim 161 162Your application may have other claims that need to be validated. For example, 163maybe the token has a `sub` claim that needs to be in a special format, or it 164requires an `email` claim to be present. Tink can't really help the verifier in 165validating these claims, as validating these is different for different types of 166tokens. We recommend validating these claims immediately after Tink has 167successfully verified the token. 168 169## Code Examples 170 171Here are some small examples on how to use Tink's JWT library: 172 173* [examples/cc/jwt](https://github.com/google/tink/tree/master/cc/examples/jwt) 174* [examples/java_src/jwt](https://github.com/google/tink/tree/master/java_src/examples/jwt) 175* [examples/python/jwt](https://github.com/google/tink/tree/master/python/examples/jwt) 176