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