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