xref: /aosp_15_r20/external/tink/docs/JWT-HOWTO.md (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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