xref: /aosp_15_r20/external/tink/cc/jwt/jwt_validator.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1*e7b1675dSTing-Kang Chang // Copyright 2021 Google LLC
2*e7b1675dSTing-Kang Chang //
3*e7b1675dSTing-Kang Chang // Licensed under the Apache License, Version 2.0 (the "License");
4*e7b1675dSTing-Kang Chang // you may not use this file except in compliance with the License.
5*e7b1675dSTing-Kang Chang // You may obtain a copy of the License at
6*e7b1675dSTing-Kang Chang //
7*e7b1675dSTing-Kang Chang //     http://www.apache.org/licenses/LICENSE-2.0
8*e7b1675dSTing-Kang Chang //
9*e7b1675dSTing-Kang Chang // Unless required by applicable law or agreed to in writing, software
10*e7b1675dSTing-Kang Chang // distributed under the License is distributed on an "AS IS" BASIS,
11*e7b1675dSTing-Kang Chang // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e7b1675dSTing-Kang Chang // See the License for the specific language governing permissions and
13*e7b1675dSTing-Kang Chang // limitations under the License.
14*e7b1675dSTing-Kang Chang //
15*e7b1675dSTing-Kang Chang ///////////////////////////////////////////////////////////////////////////////
16*e7b1675dSTing-Kang Chang 
17*e7b1675dSTing-Kang Chang #include "tink/jwt/jwt_validator.h"
18*e7b1675dSTing-Kang Chang 
19*e7b1675dSTing-Kang Chang #include <algorithm>
20*e7b1675dSTing-Kang Chang #include <string>
21*e7b1675dSTing-Kang Chang #include <vector>
22*e7b1675dSTing-Kang Chang 
23*e7b1675dSTing-Kang Chang #include "absl/status/status.h"
24*e7b1675dSTing-Kang Chang 
25*e7b1675dSTing-Kang Chang namespace crypto {
26*e7b1675dSTing-Kang Chang namespace tink {
27*e7b1675dSTing-Kang Chang 
28*e7b1675dSTing-Kang Chang namespace {
29*e7b1675dSTing-Kang Chang 
30*e7b1675dSTing-Kang Chang static constexpr absl::Duration kJwtMaxClockSkew = absl::Minutes(10);
31*e7b1675dSTing-Kang Chang 
32*e7b1675dSTing-Kang Chang }
33*e7b1675dSTing-Kang Chang 
JwtValidator(const JwtValidatorBuilder & builder)34*e7b1675dSTing-Kang Chang JwtValidator::JwtValidator(const JwtValidatorBuilder& builder) {
35*e7b1675dSTing-Kang Chang   expected_type_header_ = builder.expected_type_header_;
36*e7b1675dSTing-Kang Chang   expected_issuer_ = builder.expected_issuer_;
37*e7b1675dSTing-Kang Chang   expected_audience_ = builder.expected_audience_;
38*e7b1675dSTing-Kang Chang   ignore_type_header_ = builder.ignore_type_header_;
39*e7b1675dSTing-Kang Chang   ignore_issuer_ = builder.ignore_issuer_;
40*e7b1675dSTing-Kang Chang   ignore_audiences_ = builder.ignore_audiences_;
41*e7b1675dSTing-Kang Chang   allow_missing_expiration_ = builder.allow_missing_expiration_;
42*e7b1675dSTing-Kang Chang   expect_issued_in_the_past_ = builder.expect_issued_in_the_past_;
43*e7b1675dSTing-Kang Chang   clock_skew_ = builder.clock_skew_;
44*e7b1675dSTing-Kang Chang   fixed_now_ = builder.fixed_now_;
45*e7b1675dSTing-Kang Chang }
46*e7b1675dSTing-Kang Chang 
ValidateTimestamps(RawJwt const & raw_jwt) const47*e7b1675dSTing-Kang Chang util::Status JwtValidator::ValidateTimestamps(RawJwt const& raw_jwt) const {
48*e7b1675dSTing-Kang Chang   absl::Time now;
49*e7b1675dSTing-Kang Chang   if (fixed_now_.has_value()) {
50*e7b1675dSTing-Kang Chang     now = fixed_now_.value();
51*e7b1675dSTing-Kang Chang   } else {
52*e7b1675dSTing-Kang Chang     now = absl::Now();
53*e7b1675dSTing-Kang Chang   }
54*e7b1675dSTing-Kang Chang   if (!raw_jwt.HasExpiration() && !allow_missing_expiration_) {
55*e7b1675dSTing-Kang Chang     return util::Status(absl::StatusCode::kInvalidArgument,
56*e7b1675dSTing-Kang Chang                         "token does not have an expiration set");
57*e7b1675dSTing-Kang Chang   }
58*e7b1675dSTing-Kang Chang   if (raw_jwt.HasExpiration()) {
59*e7b1675dSTing-Kang Chang     util::StatusOr<absl::Time> expiration = raw_jwt.GetExpiration();
60*e7b1675dSTing-Kang Chang     if (!expiration.ok()) {
61*e7b1675dSTing-Kang Chang       return expiration.status();
62*e7b1675dSTing-Kang Chang     }
63*e7b1675dSTing-Kang Chang     if (*expiration <= now - clock_skew_) {
64*e7b1675dSTing-Kang Chang       return util::Status(absl::StatusCode::kInvalidArgument,
65*e7b1675dSTing-Kang Chang                           "token has expired");
66*e7b1675dSTing-Kang Chang     }
67*e7b1675dSTing-Kang Chang   }
68*e7b1675dSTing-Kang Chang   if (raw_jwt.HasNotBefore()) {
69*e7b1675dSTing-Kang Chang     util::StatusOr<absl::Time> not_before = raw_jwt.GetNotBefore();
70*e7b1675dSTing-Kang Chang     if (!not_before.ok()) {
71*e7b1675dSTing-Kang Chang       return not_before.status();
72*e7b1675dSTing-Kang Chang     }
73*e7b1675dSTing-Kang Chang     if (*not_before > now + clock_skew_) {
74*e7b1675dSTing-Kang Chang       return util::Status(absl::StatusCode::kInvalidArgument,
75*e7b1675dSTing-Kang Chang                           "token cannot yet be used");
76*e7b1675dSTing-Kang Chang     }
77*e7b1675dSTing-Kang Chang   }
78*e7b1675dSTing-Kang Chang   if (expect_issued_in_the_past_) {
79*e7b1675dSTing-Kang Chang     util::StatusOr<absl::Time> issued_at = raw_jwt.GetIssuedAt();
80*e7b1675dSTing-Kang Chang     if (!issued_at.ok()) {
81*e7b1675dSTing-Kang Chang       return issued_at.status();
82*e7b1675dSTing-Kang Chang     }
83*e7b1675dSTing-Kang Chang     if (*issued_at > now + clock_skew_) {
84*e7b1675dSTing-Kang Chang       return util::Status(absl::StatusCode::kInvalidArgument,
85*e7b1675dSTing-Kang Chang                           "token has an invalid iat claim in the future");
86*e7b1675dSTing-Kang Chang     }
87*e7b1675dSTing-Kang Chang   }
88*e7b1675dSTing-Kang Chang   return util::OkStatus();
89*e7b1675dSTing-Kang Chang }
90*e7b1675dSTing-Kang Chang 
ValidateTypeHeader(RawJwt const & raw_jwt) const91*e7b1675dSTing-Kang Chang util::Status JwtValidator::ValidateTypeHeader(RawJwt const& raw_jwt) const {
92*e7b1675dSTing-Kang Chang   if (expected_type_header_.has_value()) {
93*e7b1675dSTing-Kang Chang     if (!raw_jwt.HasTypeHeader()) {
94*e7b1675dSTing-Kang Chang       return util::Status(absl::StatusCode::kInvalidArgument,
95*e7b1675dSTing-Kang Chang                           "missing expected type header");
96*e7b1675dSTing-Kang Chang     }
97*e7b1675dSTing-Kang Chang     util::StatusOr<std::string> type_header = raw_jwt.GetTypeHeader();
98*e7b1675dSTing-Kang Chang     if (!type_header.ok()) {
99*e7b1675dSTing-Kang Chang       return type_header.status();
100*e7b1675dSTing-Kang Chang     }
101*e7b1675dSTing-Kang Chang     if (expected_type_header_.value() != *type_header) {
102*e7b1675dSTing-Kang Chang       return util::Status(absl::StatusCode::kInvalidArgument,
103*e7b1675dSTing-Kang Chang                           "wrong type header");
104*e7b1675dSTing-Kang Chang     }
105*e7b1675dSTing-Kang Chang   } else {
106*e7b1675dSTing-Kang Chang     if (raw_jwt.HasTypeHeader() && !ignore_type_header_) {
107*e7b1675dSTing-Kang Chang       return util::Status(
108*e7b1675dSTing-Kang Chang           absl::StatusCode::kInvalidArgument,
109*e7b1675dSTing-Kang Chang           "invalid JWT; token has type header set, but validator not");
110*e7b1675dSTing-Kang Chang     }
111*e7b1675dSTing-Kang Chang   }
112*e7b1675dSTing-Kang Chang   return util::OkStatus();
113*e7b1675dSTing-Kang Chang }
114*e7b1675dSTing-Kang Chang 
ValidateIssuer(RawJwt const & raw_jwt) const115*e7b1675dSTing-Kang Chang util::Status JwtValidator::ValidateIssuer(RawJwt const& raw_jwt) const {
116*e7b1675dSTing-Kang Chang   if (expected_issuer_.has_value()){
117*e7b1675dSTing-Kang Chang     if (!raw_jwt.HasIssuer()) {
118*e7b1675dSTing-Kang Chang       return util::Status(absl::StatusCode::kInvalidArgument,
119*e7b1675dSTing-Kang Chang                           "missing expected issuer");
120*e7b1675dSTing-Kang Chang     }
121*e7b1675dSTing-Kang Chang     util::StatusOr<std::string> issuer = raw_jwt.GetIssuer();
122*e7b1675dSTing-Kang Chang     if (!issuer.ok()) {
123*e7b1675dSTing-Kang Chang       return issuer.status();
124*e7b1675dSTing-Kang Chang     }
125*e7b1675dSTing-Kang Chang     if (expected_issuer_.value() != *issuer) {
126*e7b1675dSTing-Kang Chang       return util::Status(absl::StatusCode::kInvalidArgument, "wrong issuer");
127*e7b1675dSTing-Kang Chang     }
128*e7b1675dSTing-Kang Chang   } else {
129*e7b1675dSTing-Kang Chang     if (raw_jwt.HasIssuer() && !ignore_issuer_) {
130*e7b1675dSTing-Kang Chang       return util::Status(
131*e7b1675dSTing-Kang Chang           absl::StatusCode::kInvalidArgument,
132*e7b1675dSTing-Kang Chang           "invalid JWT; token has issuer set, but validator not");
133*e7b1675dSTing-Kang Chang     }
134*e7b1675dSTing-Kang Chang   }
135*e7b1675dSTing-Kang Chang   return util::OkStatus();
136*e7b1675dSTing-Kang Chang }
137*e7b1675dSTing-Kang Chang 
ValidateAudiences(RawJwt const & raw_jwt) const138*e7b1675dSTing-Kang Chang util::Status JwtValidator::ValidateAudiences(RawJwt const& raw_jwt) const {
139*e7b1675dSTing-Kang Chang   if (expected_audience_.has_value()) {
140*e7b1675dSTing-Kang Chang     if (!raw_jwt.HasAudiences()) {
141*e7b1675dSTing-Kang Chang       return util::Status(absl::StatusCode::kInvalidArgument,
142*e7b1675dSTing-Kang Chang                           "missing expected audiences");
143*e7b1675dSTing-Kang Chang     }
144*e7b1675dSTing-Kang Chang     util::StatusOr<std::vector<std::string>> audiences = raw_jwt.GetAudiences();
145*e7b1675dSTing-Kang Chang     if (!audiences.ok()) {
146*e7b1675dSTing-Kang Chang       return audiences.status();
147*e7b1675dSTing-Kang Chang     }
148*e7b1675dSTing-Kang Chang     auto it =
149*e7b1675dSTing-Kang Chang         std::find(audiences->begin(), audiences->end(), expected_audience_);
150*e7b1675dSTing-Kang Chang     if (it == audiences->end()) {
151*e7b1675dSTing-Kang Chang       return util::Status(absl::StatusCode::kInvalidArgument,
152*e7b1675dSTing-Kang Chang                           "audience not found");
153*e7b1675dSTing-Kang Chang     }
154*e7b1675dSTing-Kang Chang   } else {
155*e7b1675dSTing-Kang Chang     if (raw_jwt.HasAudiences() && !ignore_audiences_) {
156*e7b1675dSTing-Kang Chang       return util::Status(
157*e7b1675dSTing-Kang Chang           absl::StatusCode::kInvalidArgument,
158*e7b1675dSTing-Kang Chang           "invalid JWT; token has audience set, but validator not");
159*e7b1675dSTing-Kang Chang     }
160*e7b1675dSTing-Kang Chang   }
161*e7b1675dSTing-Kang Chang   return util::OkStatus();
162*e7b1675dSTing-Kang Chang }
163*e7b1675dSTing-Kang Chang 
Validate(RawJwt const & raw_jwt) const164*e7b1675dSTing-Kang Chang util::Status JwtValidator::Validate(RawJwt const& raw_jwt) const {
165*e7b1675dSTing-Kang Chang   util::Status status;
166*e7b1675dSTing-Kang Chang   status = ValidateTimestamps(raw_jwt);
167*e7b1675dSTing-Kang Chang   if (!status.ok()) {
168*e7b1675dSTing-Kang Chang     return status;
169*e7b1675dSTing-Kang Chang   }
170*e7b1675dSTing-Kang Chang   status = ValidateTypeHeader(raw_jwt);
171*e7b1675dSTing-Kang Chang   if (!status.ok()) {
172*e7b1675dSTing-Kang Chang     return status;
173*e7b1675dSTing-Kang Chang   }
174*e7b1675dSTing-Kang Chang   status = ValidateIssuer(raw_jwt);
175*e7b1675dSTing-Kang Chang   if (!status.ok()) {
176*e7b1675dSTing-Kang Chang     return status;
177*e7b1675dSTing-Kang Chang   }
178*e7b1675dSTing-Kang Chang   status = ValidateAudiences(raw_jwt);
179*e7b1675dSTing-Kang Chang   if (!status.ok()) {
180*e7b1675dSTing-Kang Chang     return status;
181*e7b1675dSTing-Kang Chang   }
182*e7b1675dSTing-Kang Chang   return util::OkStatus();
183*e7b1675dSTing-Kang Chang }
184*e7b1675dSTing-Kang Chang 
JwtValidatorBuilder()185*e7b1675dSTing-Kang Chang JwtValidatorBuilder::JwtValidatorBuilder() {
186*e7b1675dSTing-Kang Chang   ignore_type_header_ = false;
187*e7b1675dSTing-Kang Chang   ignore_issuer_ = false;
188*e7b1675dSTing-Kang Chang   ignore_audiences_ = false;
189*e7b1675dSTing-Kang Chang   allow_missing_expiration_ = false;
190*e7b1675dSTing-Kang Chang   expect_issued_in_the_past_ = false;
191*e7b1675dSTing-Kang Chang   clock_skew_ = absl::ZeroDuration();
192*e7b1675dSTing-Kang Chang }
193*e7b1675dSTing-Kang Chang 
ExpectTypeHeader(absl::string_view type_header)194*e7b1675dSTing-Kang Chang JwtValidatorBuilder& JwtValidatorBuilder::ExpectTypeHeader(
195*e7b1675dSTing-Kang Chang     absl::string_view type_header) {
196*e7b1675dSTing-Kang Chang   expected_type_header_ = std::string(type_header);
197*e7b1675dSTing-Kang Chang   return *this;
198*e7b1675dSTing-Kang Chang }
199*e7b1675dSTing-Kang Chang 
ExpectIssuer(absl::string_view issuer)200*e7b1675dSTing-Kang Chang JwtValidatorBuilder& JwtValidatorBuilder::ExpectIssuer(
201*e7b1675dSTing-Kang Chang     absl::string_view issuer) {
202*e7b1675dSTing-Kang Chang   expected_issuer_ = std::string(issuer);
203*e7b1675dSTing-Kang Chang   return *this;
204*e7b1675dSTing-Kang Chang }
205*e7b1675dSTing-Kang Chang 
ExpectAudience(absl::string_view audience)206*e7b1675dSTing-Kang Chang JwtValidatorBuilder& JwtValidatorBuilder::ExpectAudience(
207*e7b1675dSTing-Kang Chang     absl::string_view audience) {
208*e7b1675dSTing-Kang Chang   expected_audience_ = std::string(audience);
209*e7b1675dSTing-Kang Chang   return *this;
210*e7b1675dSTing-Kang Chang }
211*e7b1675dSTing-Kang Chang 
IgnoreTypeHeader()212*e7b1675dSTing-Kang Chang JwtValidatorBuilder& JwtValidatorBuilder::IgnoreTypeHeader() {
213*e7b1675dSTing-Kang Chang   ignore_type_header_ = true;
214*e7b1675dSTing-Kang Chang   return *this;
215*e7b1675dSTing-Kang Chang }
216*e7b1675dSTing-Kang Chang 
IgnoreIssuer()217*e7b1675dSTing-Kang Chang JwtValidatorBuilder& JwtValidatorBuilder::IgnoreIssuer() {
218*e7b1675dSTing-Kang Chang   ignore_issuer_ = true;
219*e7b1675dSTing-Kang Chang   return *this;
220*e7b1675dSTing-Kang Chang }
221*e7b1675dSTing-Kang Chang 
IgnoreAudiences()222*e7b1675dSTing-Kang Chang JwtValidatorBuilder& JwtValidatorBuilder::IgnoreAudiences() {
223*e7b1675dSTing-Kang Chang   ignore_audiences_ = true;
224*e7b1675dSTing-Kang Chang   return *this;
225*e7b1675dSTing-Kang Chang }
226*e7b1675dSTing-Kang Chang 
AllowMissingExpiration()227*e7b1675dSTing-Kang Chang JwtValidatorBuilder& JwtValidatorBuilder::AllowMissingExpiration() {
228*e7b1675dSTing-Kang Chang   allow_missing_expiration_ = true;
229*e7b1675dSTing-Kang Chang   return *this;
230*e7b1675dSTing-Kang Chang }
231*e7b1675dSTing-Kang Chang 
ExpectIssuedInThePast()232*e7b1675dSTing-Kang Chang JwtValidatorBuilder& JwtValidatorBuilder::ExpectIssuedInThePast() {
233*e7b1675dSTing-Kang Chang   expect_issued_in_the_past_ = true;
234*e7b1675dSTing-Kang Chang   return *this;
235*e7b1675dSTing-Kang Chang }
236*e7b1675dSTing-Kang Chang 
SetClockSkew(absl::Duration clock_skew)237*e7b1675dSTing-Kang Chang JwtValidatorBuilder& JwtValidatorBuilder::SetClockSkew(
238*e7b1675dSTing-Kang Chang     absl::Duration clock_skew) {
239*e7b1675dSTing-Kang Chang   clock_skew_ = clock_skew;
240*e7b1675dSTing-Kang Chang   return *this;
241*e7b1675dSTing-Kang Chang }
242*e7b1675dSTing-Kang Chang 
SetFixedNow(absl::Time fixed_now)243*e7b1675dSTing-Kang Chang JwtValidatorBuilder& JwtValidatorBuilder::SetFixedNow(absl::Time fixed_now) {
244*e7b1675dSTing-Kang Chang   fixed_now_ = fixed_now;
245*e7b1675dSTing-Kang Chang   return *this;
246*e7b1675dSTing-Kang Chang }
247*e7b1675dSTing-Kang Chang 
Build()248*e7b1675dSTing-Kang Chang util::StatusOr<JwtValidator> JwtValidatorBuilder::Build() {
249*e7b1675dSTing-Kang Chang   if (expected_type_header_.has_value() && ignore_type_header_) {
250*e7b1675dSTing-Kang Chang     return util::Status(
251*e7b1675dSTing-Kang Chang         absl::StatusCode::kInvalidArgument,
252*e7b1675dSTing-Kang Chang         "IgnoreTypeHeader() and ExpectTypeHeader() cannot be used together");
253*e7b1675dSTing-Kang Chang   }
254*e7b1675dSTing-Kang Chang   if (expected_issuer_.has_value() && ignore_issuer_) {
255*e7b1675dSTing-Kang Chang     return util::Status(
256*e7b1675dSTing-Kang Chang         absl::StatusCode::kInvalidArgument,
257*e7b1675dSTing-Kang Chang         "IgnoreIssuer() and ExpectedIssuer() cannot be used together");
258*e7b1675dSTing-Kang Chang   }
259*e7b1675dSTing-Kang Chang   if (expected_audience_.has_value() && ignore_audiences_) {
260*e7b1675dSTing-Kang Chang     return util::Status(
261*e7b1675dSTing-Kang Chang         absl::StatusCode::kInvalidArgument,
262*e7b1675dSTing-Kang Chang         "IgnoreAudiences() and ExpectAudience() cannot be used together");
263*e7b1675dSTing-Kang Chang   }
264*e7b1675dSTing-Kang Chang   if (clock_skew_ > kJwtMaxClockSkew) {
265*e7b1675dSTing-Kang Chang     return util::Status(absl::StatusCode::kInvalidArgument,
266*e7b1675dSTing-Kang Chang                         "clock skew too large, max is 10 minutes");
267*e7b1675dSTing-Kang Chang   }
268*e7b1675dSTing-Kang Chang   JwtValidator validator(*this);
269*e7b1675dSTing-Kang Chang   return validator;
270*e7b1675dSTing-Kang Chang }
271*e7b1675dSTing-Kang Chang 
272*e7b1675dSTing-Kang Chang }  // namespace tink
273*e7b1675dSTing-Kang Chang }  // namespace crypto
274*e7b1675dSTing-Kang Chang 
275