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