xref: /aosp_15_r20/external/tink/cc/jwt/raw_jwt.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/raw_jwt.h"
18 
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/status/status.h"
24 #include "absl/strings/numbers.h"
25 #include "absl/strings/str_format.h"
26 #include "absl/strings/substitute.h"
27 #include "absl/time/time.h"
28 #include "tink/jwt/internal/json_util.h"
29 
30 namespace crypto {
31 namespace tink {
32 
33 namespace {
34 
35 using ::google::protobuf::Struct;
36 using ::google::protobuf::Value;
37 
38 // Registered claim names, as defined in
39 // https://tools.ietf.org/html/rfc7519#section-4.1.
40 constexpr absl::string_view kJwtClaimIssuer = "iss";
41 constexpr absl::string_view kJwtClaimSubject = "sub";
42 constexpr absl::string_view kJwtClaimAudience = "aud";
43 constexpr absl::string_view kJwtClaimExpiration = "exp";
44 constexpr absl::string_view kJwtClaimNotBefore = "nbf";
45 constexpr absl::string_view kJwtClaimIssuedAt = "iat";
46 constexpr absl::string_view kJwtClaimJwtId = "jti";
47 
48 constexpr int64_t kJwtTimestampMax = 253402300799;  // 31 Dec 9999, 23:59:59 GMT
49 
IsRegisteredClaimName(absl::string_view name)50 bool IsRegisteredClaimName(absl::string_view name) {
51   return name == kJwtClaimIssuer || name == kJwtClaimSubject ||
52          name == kJwtClaimAudience || name == kJwtClaimExpiration ||
53          name == kJwtClaimNotBefore || name == kJwtClaimIssuedAt ||
54          name == kJwtClaimJwtId;
55 }
56 
ValidatePayloadName(absl::string_view name)57 util::Status ValidatePayloadName(absl::string_view name) {
58   if (IsRegisteredClaimName(name)) {
59     return absl::InvalidArgumentError(absl::Substitute(
60         "claim '$0' is invalid because it's a registered name; "
61         "use the corresponding getter or setter method.",
62         name));
63   }
64   return util::OkStatus();
65 }
66 
HasClaimOfKind(const google::protobuf::Struct & json_proto,absl::string_view name,Value::KindCase kind)67 bool HasClaimOfKind(const google::protobuf::Struct& json_proto,
68                     absl::string_view name, Value::KindCase kind) {
69   if (IsRegisteredClaimName(name)) {
70     return false;
71   }
72   const auto& fields = json_proto.fields();
73   auto it = fields.find(std::string(name));
74   if (it == fields.end()) {
75     return false;
76   }
77   const Value& value = it->second;
78   return value.kind_case() == kind;
79 }
80 
81 // Returns true if the claim is present but not a string.
ClaimIsNotAString(const google::protobuf::Struct & json_proto,absl::string_view name)82 bool ClaimIsNotAString(const google::protobuf::Struct& json_proto,
83                        absl::string_view name) {
84   const auto& fields = json_proto.fields();
85   auto it = fields.find(std::string(name));
86   if (it == fields.end()) {
87     return false;
88   }
89   const Value& value = it->second;
90   return value.kind_case() != Value::kStringValue;
91 }
92 
93 // Returns true if the claim is present but not a list.
ClaimIsNotAList(google::protobuf::Struct & json_proto,absl::string_view name)94 bool ClaimIsNotAList(google::protobuf::Struct& json_proto,
95                      absl::string_view name) {
96   const auto& fields = json_proto.fields();
97   auto it = fields.find(std::string(name));
98   if (it == fields.end()) {
99     return false;
100   }
101   const Value& value = it->second;
102   return value.kind_case() != Value::kListValue;
103 }
104 
105 // Returns true if the claim is present but not a timestamp.
ClaimIsNotATimestamp(const google::protobuf::Struct & json_proto,absl::string_view name)106 bool ClaimIsNotATimestamp(const google::protobuf::Struct& json_proto,
107                           absl::string_view name) {
108   const auto& fields = json_proto.fields();
109   auto it = fields.find(std::string(name));
110   if (it == fields.end()) {
111     return false;
112   }
113   const Value& value = it->second;
114   if (value.kind_case() != Value::kNumberValue) {
115     return true;
116   }
117   double timestamp = value.number_value();
118   return (timestamp > kJwtTimestampMax) || (timestamp < 0);
119 }
120 
TimeToTimestamp(absl::Time time)121 int64_t TimeToTimestamp(absl::Time time) {
122   // We round the timestamp to a whole number. We always round down.
123   return absl::ToUnixSeconds(time);
124 }
125 
TimestampToTime(double timestamp)126 absl::Time TimestampToTime(double timestamp) {
127   if (timestamp > kJwtTimestampMax) {
128     return absl::FromUnixSeconds(kJwtTimestampMax);
129   }
130   return absl::FromUnixSeconds(timestamp);
131 }
132 
ValidateAudienceClaim(const google::protobuf::Struct & json_proto)133 util::Status ValidateAudienceClaim(const google::protobuf::Struct& json_proto) {
134   const auto& fields = json_proto.fields();
135   auto it = fields.find(std::string(kJwtClaimAudience));
136   if (it == fields.end()) {
137     return util::OkStatus();
138   }
139   const Value& value = it->second;
140   if (value.kind_case() == Value::kStringValue) {
141     return util::OkStatus();
142   }
143   if (value.kind_case() != Value::kListValue) {
144     return util::Status(absl::StatusCode::kInvalidArgument,
145                         "aud claim is not a list");
146   }
147   if (value.list_value().values_size() < 1) {
148     return util::Status(absl::StatusCode::kInvalidArgument,
149                         "aud claim is present but empty");
150   }
151   for (const Value& v : value.list_value().values()) {
152     if (v.kind_case() != Value::kStringValue) {
153       return util::Status(absl::StatusCode::kInvalidArgument,
154                           "aud claim is not a list of strings");
155     }
156   }
157   return util::OkStatus();
158 }
159 
160 }  // namespace
161 
FromJson(absl::optional<std::string> type_header,absl::string_view json_payload)162 util::StatusOr<RawJwt> RawJwt::FromJson(absl::optional<std::string> type_header,
163                                         absl::string_view json_payload) {
164   util::StatusOr<google::protobuf::Struct> proto =
165       jwt_internal::JsonStringToProtoStruct(json_payload);
166   if (!proto.ok()) {
167     return proto.status();
168   }
169   if (ClaimIsNotAString(*proto, kJwtClaimIssuer) ||
170       ClaimIsNotAString(*proto, kJwtClaimSubject) ||
171       ClaimIsNotATimestamp(*proto, kJwtClaimExpiration) ||
172       ClaimIsNotATimestamp(*proto, kJwtClaimNotBefore) ||
173       ClaimIsNotATimestamp(*proto, kJwtClaimIssuedAt)) {
174     return util::Status(absl::StatusCode::kInvalidArgument,
175                         "contains an invalid registered claim");
176   }
177   util::Status aud_status = ValidateAudienceClaim(*proto);
178   if (!aud_status.ok()) {
179     return aud_status;
180   }
181   RawJwt token(type_header, *std::move(proto));
182   return token;
183 }
184 
GetJsonPayload() const185 util::StatusOr<std::string> RawJwt::GetJsonPayload() const {
186   return jwt_internal::ProtoStructToJsonString(json_proto_);
187 }
188 
189 RawJwt::RawJwt() = default;
190 
RawJwt(absl::optional<std::string> type_header,google::protobuf::Struct json_proto)191 RawJwt::RawJwt(absl::optional<std::string> type_header,
192                google::protobuf::Struct json_proto) {
193   type_header_ = type_header;
194   json_proto_ = json_proto;
195 }
196 
HasTypeHeader() const197 bool RawJwt::HasTypeHeader() const { return type_header_.has_value(); }
198 
GetTypeHeader() const199 util::StatusOr<std::string> RawJwt::GetTypeHeader() const {
200   if (!type_header_.has_value()) {
201     return util::Status(absl::StatusCode::kInvalidArgument,
202                         "No type header found");
203   }
204   return *type_header_;
205 }
206 
HasIssuer() const207 bool RawJwt::HasIssuer() const {
208   return json_proto_.fields().contains(std::string(kJwtClaimIssuer));
209 }
210 
GetIssuer() const211 util::StatusOr<std::string> RawJwt::GetIssuer() const {
212   const auto& fields = json_proto_.fields();
213   auto it = fields.find(std::string(kJwtClaimIssuer));
214   if (it == fields.end()) {
215     return util::Status(absl::StatusCode::kInvalidArgument, "No Issuer found");
216   }
217   const Value& value = it->second;
218   if (value.kind_case() != Value::kStringValue) {
219     return util::Status(absl::StatusCode::kInvalidArgument,
220                         "Issuer is not a string");
221   }
222   return value.string_value();
223 }
224 
HasSubject() const225 bool RawJwt::HasSubject() const {
226   return json_proto_.fields().contains(std::string(kJwtClaimSubject));
227 }
228 
GetSubject() const229 util::StatusOr<std::string> RawJwt::GetSubject() const {
230   const auto& fields = json_proto_.fields();
231   auto it = fields.find(std::string(kJwtClaimSubject));
232   if (it == fields.end()) {
233     return util::Status(absl::StatusCode::kInvalidArgument, "No Subject found");
234   }
235   const Value& value = it->second;
236   if (value.kind_case() != Value::kStringValue) {
237     return util::Status(absl::StatusCode::kInvalidArgument,
238                         "Subject is not a string");
239   }
240   return value.string_value();
241 }
242 
HasAudiences() const243 bool RawJwt::HasAudiences() const {
244   return json_proto_.fields().contains(std::string(kJwtClaimAudience));
245 }
246 
GetAudiences() const247 util::StatusOr<std::vector<std::string>> RawJwt::GetAudiences() const {
248   const auto& fields = json_proto_.fields();
249   auto it = fields.find(std::string(kJwtClaimAudience));
250   if (it == fields.end()) {
251     return util::Status(absl::StatusCode::kNotFound, "No Audiences found");
252   }
253   Value list = it->second;
254   if (list.kind_case() != Value::kListValue) {
255     std::vector<std::string> audiences;
256     audiences.push_back(list.string_value());
257     return audiences;
258   }
259   if (list.kind_case() != Value::kListValue) {
260     return util::Status(absl::StatusCode::kInvalidArgument,
261                         "Audiences is not a list");
262   }
263   std::vector<std::string> audiences;
264   for (const auto& value : list.list_value().values()) {
265     if (value.kind_case() != Value::kStringValue) {
266       return util::Status(absl::StatusCode::kInvalidArgument,
267                           "Audiences is not a list of strings");
268     }
269     audiences.push_back(value.string_value());
270   }
271   return audiences;
272 }
273 
HasJwtId() const274 bool RawJwt::HasJwtId() const {
275   return json_proto_.fields().contains(std::string(kJwtClaimJwtId));
276 }
277 
GetJwtId() const278 util::StatusOr<std::string> RawJwt::GetJwtId() const {
279   const auto& fields = json_proto_.fields();
280   auto it = fields.find(std::string(kJwtClaimJwtId));
281   if (it == fields.end()) {
282     return util::Status(absl::StatusCode::kNotFound, "No JwtId found");
283   }
284   const Value& value = it->second;
285   if (value.kind_case() != Value::kStringValue) {
286     return util::Status(absl::StatusCode::kInvalidArgument,
287                         "JwtId is not a string");
288   }
289   return value.string_value();
290 }
291 
HasExpiration() const292 bool RawJwt::HasExpiration() const {
293   return json_proto_.fields().contains(std::string(kJwtClaimExpiration));
294 }
295 
GetExpiration() const296 util::StatusOr<absl::Time> RawJwt::GetExpiration() const {
297   const auto& fields = json_proto_.fields();
298   auto it = fields.find(std::string(kJwtClaimExpiration));
299   if (it == fields.end()) {
300     return util::Status(absl::StatusCode::kNotFound, "No Expiration found");
301   }
302   const Value& value = it->second;
303   if (value.kind_case() != Value::kNumberValue) {
304     return util::Status(absl::StatusCode::kInvalidArgument,
305                         "Expiration is not a number");
306   }
307   return TimestampToTime(value.number_value());
308 }
309 
HasNotBefore() const310 bool RawJwt::HasNotBefore() const {
311   return json_proto_.fields().contains(std::string(kJwtClaimNotBefore));
312 }
313 
GetNotBefore() const314 util::StatusOr<absl::Time> RawJwt::GetNotBefore() const {
315   const auto& fields = json_proto_.fields();
316   auto it = fields.find(std::string(kJwtClaimNotBefore));
317   if (it == fields.end()) {
318     return util::Status(absl::StatusCode::kNotFound, "No NotBefore found");
319   }
320   const Value& value = it->second;
321   if (value.kind_case() != Value::kNumberValue) {
322     return util::Status(absl::StatusCode::kInvalidArgument,
323                         "NotBefore is not a number");
324   }
325   return TimestampToTime(value.number_value());
326 }
327 
HasIssuedAt() const328 bool RawJwt::HasIssuedAt() const {
329   return json_proto_.fields().contains(std::string(kJwtClaimIssuedAt));
330 }
331 
GetIssuedAt() const332 util::StatusOr<absl::Time> RawJwt::GetIssuedAt() const {
333   const auto& fields = json_proto_.fields();
334   auto it = fields.find(std::string(kJwtClaimIssuedAt));
335   if (it == fields.end()) {
336     return util::Status(absl::StatusCode::kNotFound, "No IssuedAt found");
337   }
338   const Value& value = it->second;
339   if (value.kind_case() != Value::kNumberValue) {
340     return util::Status(absl::StatusCode::kInvalidArgument,
341                         "IssuedAt is not a number");
342   }
343   return TimestampToTime(value.number_value());
344 }
345 
IsNullClaim(absl::string_view name) const346 bool RawJwt::IsNullClaim(absl::string_view name) const {
347   return HasClaimOfKind(json_proto_, name, Value::kNullValue);
348 }
349 
HasBooleanClaim(absl::string_view name) const350 bool RawJwt::HasBooleanClaim(absl::string_view name) const {
351   return HasClaimOfKind(json_proto_, name, Value::kBoolValue);
352 }
353 
GetBooleanClaim(absl::string_view name) const354 util::StatusOr<bool> RawJwt::GetBooleanClaim(
355     absl::string_view name) const {
356   util::Status status = ValidatePayloadName(name);
357   if (!status.ok()) {
358     return status;
359   }
360   const auto& fields = json_proto_.fields();
361   auto it = fields.find(std::string(name));
362   if (it == fields.end()) {
363     return util::Status(absl::StatusCode::kNotFound,
364                         absl::Substitute("claim '$0' not found", name));
365   }
366   const Value& value = it->second;
367   if (value.kind_case() != Value::kBoolValue) {
368     return util::Status(absl::StatusCode::kInvalidArgument,
369                         absl::Substitute("claim '$0' is not a bool", name));
370   }
371   return value.bool_value();
372 }
373 
HasStringClaim(absl::string_view name) const374 bool RawJwt::HasStringClaim(absl::string_view name) const {
375   return HasClaimOfKind(json_proto_, name, Value::kStringValue);
376 }
377 
GetStringClaim(absl::string_view name) const378 util::StatusOr<std::string> RawJwt::GetStringClaim(
379     absl::string_view name) const {
380   util::Status status = ValidatePayloadName(name);
381   if (!status.ok()) {
382     return status;
383   }
384   const auto& fields = json_proto_.fields();
385   auto it = fields.find(std::string(name));
386   if (it == fields.end()) {
387     return util::Status(absl::StatusCode::kNotFound,
388                         absl::Substitute("claim '$0' not found", name));
389   }
390   const Value& value = it->second;
391   if (value.kind_case() != Value::kStringValue) {
392     return util::Status(absl::StatusCode::kInvalidArgument,
393                         absl::Substitute("claim '$0' is not a string", name));
394   }
395   return value.string_value();
396 }
397 
HasNumberClaim(absl::string_view name) const398 bool RawJwt::HasNumberClaim(absl::string_view name) const {
399   return HasClaimOfKind(json_proto_, name, Value::kNumberValue);
400 }
401 
GetNumberClaim(absl::string_view name) const402 util::StatusOr<double> RawJwt::GetNumberClaim(absl::string_view name) const {
403   util::Status status = ValidatePayloadName(name);
404   if (!status.ok()) {
405     return status;
406   }
407   const auto& fields = json_proto_.fields();
408   auto it = fields.find(std::string(name));
409   if (it == fields.end()) {
410     return util::Status(absl::StatusCode::kNotFound,
411                         absl::Substitute("claim '$0' not found", name));
412   }
413   const Value& value = it->second;
414   if (value.kind_case() != Value::kNumberValue) {
415     return util::Status(absl::StatusCode::kInvalidArgument,
416                         absl::Substitute("claim '$0' is not a number", name));
417   }
418   return value.number_value();
419 }
420 
HasJsonObjectClaim(absl::string_view name) const421 bool RawJwt::HasJsonObjectClaim(absl::string_view name) const {
422   return HasClaimOfKind(json_proto_, name, Value::kStructValue);
423 }
424 
GetJsonObjectClaim(absl::string_view name) const425 util::StatusOr<std::string> RawJwt::GetJsonObjectClaim(
426     absl::string_view name) const {
427   util::Status status = ValidatePayloadName(name);
428   if (!status.ok()) {
429     return status;
430   }
431   const auto& fields = json_proto_.fields();
432   auto it = fields.find(std::string(name));
433   if (it == fields.end()) {
434     return util::Status(absl::StatusCode::kNotFound,
435                         absl::Substitute("claim '$0' not found", name));
436   }
437   const Value& value = it->second;
438   if (value.kind_case() != Value::kStructValue) {
439     return util::Status(
440         absl::StatusCode::kInvalidArgument,
441         absl::Substitute("claim '$0' is not a JSON object", name));
442   }
443   return jwt_internal::ProtoStructToJsonString(value.struct_value());
444 }
445 
HasJsonArrayClaim(absl::string_view name) const446 bool RawJwt::HasJsonArrayClaim(absl::string_view name) const {
447   return HasClaimOfKind(json_proto_, name, Value::kListValue);
448 }
449 
GetJsonArrayClaim(absl::string_view name) const450 util::StatusOr<std::string> RawJwt::GetJsonArrayClaim(
451     absl::string_view name) const {
452   util::Status status = ValidatePayloadName(name);
453   if (!status.ok()) {
454     return status;
455   }
456   const auto& fields = json_proto_.fields();
457   auto it = fields.find(std::string(name));
458   if (it == fields.end()) {
459     return util::Status(absl::StatusCode::kNotFound,
460                         absl::Substitute("claim '$0' not found", name));
461   }
462   const Value& value = it->second;
463   if (value.kind_case() != Value::kListValue) {
464     return util::Status(
465         absl::StatusCode::kInvalidArgument,
466         absl::Substitute("claim '$0' is not a JSON array", name));
467   }
468   return jwt_internal::ProtoListToJsonString(value.list_value());
469 }
470 
CustomClaimNames() const471 std::vector<std::string> RawJwt::CustomClaimNames() const {
472   const auto& fields = json_proto_.fields();
473   std::vector<std::string> values;
474   for (auto it = fields.begin(); it != fields.end(); it++) {
475     if (!IsRegisteredClaimName(it->first)) {
476       values.push_back(it->first);
477     }
478   }
479   return values;
480 }
481 
RawJwtBuilder()482 RawJwtBuilder::RawJwtBuilder() { without_expiration_ = false; }
483 
SetTypeHeader(absl::string_view type_header)484 RawJwtBuilder& RawJwtBuilder::SetTypeHeader(absl::string_view type_header) {
485   type_header_ = std::string(type_header);
486   return *this;
487 }
488 
SetIssuer(absl::string_view issuer)489 RawJwtBuilder& RawJwtBuilder::SetIssuer(absl::string_view issuer) {
490   auto fields = json_proto_.mutable_fields();
491   Value value;
492   value.set_string_value(std::string(issuer));
493   (*fields)[std::string(kJwtClaimIssuer)] = value;
494   return *this;
495 }
496 
SetSubject(absl::string_view subject)497 RawJwtBuilder& RawJwtBuilder::SetSubject(absl::string_view subject) {
498   auto fields = json_proto_.mutable_fields();
499   Value value;
500   value.set_string_value(std::string(subject));
501   (*fields)[std::string(kJwtClaimSubject)] = value;
502   return *this;
503 }
504 
SetAudience(absl::string_view audience)505 RawJwtBuilder& RawJwtBuilder::SetAudience(absl::string_view audience) {
506   // Make sure that "aud" is not already a list by a call to SetAudiences or
507   // AddAudience.
508   if (ClaimIsNotAString(json_proto_, kJwtClaimAudience)) {
509     error_ = util::Status(absl::StatusCode::kInvalidArgument,
510                           "SetAudience() must not be called together with "
511                           "SetAudiences() or AddAudience");
512     return *this;
513   }
514   auto fields = json_proto_.mutable_fields();
515   Value value;
516   value.set_string_value(std::string(audience));
517   (*fields)[std::string(kJwtClaimAudience)] = value;
518   return *this;
519 }
520 
SetAudiences(std::vector<std::string> audiences)521 RawJwtBuilder& RawJwtBuilder::SetAudiences(std::vector<std::string> audiences) {
522   // Make sure that "aud" is not already a string by a call to SetAudience.
523   if (ClaimIsNotAList(json_proto_, kJwtClaimAudience)) {
524     error_ = util::Status(
525         absl::StatusCode::kInvalidArgument,
526         "SetAudiences() and SetAudience() must not be called together");
527     return *this;
528   }
529   auto fields = json_proto_.mutable_fields();
530   Value value;
531   for (const auto& audience : audiences) {
532     value.mutable_list_value()->add_values()->set_string_value(audience);
533   }
534   (*fields)[std::string(kJwtClaimAudience)] = value;
535   return *this;
536 }
537 
AddAudience(absl::string_view audience)538 RawJwtBuilder& RawJwtBuilder::AddAudience(absl::string_view audience) {
539   // Make sure that "aud" is not already a string by a call to SetAudience.
540   if (ClaimIsNotAList(json_proto_, kJwtClaimAudience)) {
541     error_ = util::Status(
542         absl::StatusCode::kInvalidArgument,
543         "AddAudience() and SetAudience() must not be called together");
544     return *this;
545   }
546   auto fields = json_proto_.mutable_fields();
547   auto insertion_result =
548       fields->insert({std::string(kJwtClaimAudience), Value()});
549   google::protobuf::ListValue* list_value =
550       insertion_result.first->second.mutable_list_value();
551   list_value->add_values()->set_string_value(std::string(audience));
552   return *this;
553 }
554 
SetJwtId(absl::string_view jwid)555 RawJwtBuilder& RawJwtBuilder::SetJwtId(absl::string_view jwid) {
556   auto fields = json_proto_.mutable_fields();
557   Value value;
558   value.set_string_value(std::string(jwid));
559   (*fields)[std::string(kJwtClaimJwtId)] = value;
560   return *this;
561 }
562 
WithoutExpiration()563 RawJwtBuilder& RawJwtBuilder::WithoutExpiration() {
564   without_expiration_ = true;
565   return *this;
566 }
567 
SetExpiration(absl::Time expiration)568 RawJwtBuilder& RawJwtBuilder::SetExpiration(absl::Time expiration) {
569   int64_t exp_timestamp = TimeToTimestamp(expiration);
570   if ((exp_timestamp > kJwtTimestampMax) || (exp_timestamp < 0)) {
571     if (!error_.has_value()) {
572       error_ = util::Status(absl::StatusCode::kInvalidArgument,
573                             "invalid expiration timestamp");
574     }
575     return *this;
576   }
577   auto fields = json_proto_.mutable_fields();
578   Value value;
579   value.set_number_value(exp_timestamp);
580   (*fields)[std::string(kJwtClaimExpiration)] = value;
581   return *this;
582 }
583 
SetNotBefore(absl::Time not_before)584 RawJwtBuilder& RawJwtBuilder::SetNotBefore(absl::Time not_before) {
585   int64_t nbf_timestamp = TimeToTimestamp(not_before);
586   if ((nbf_timestamp > kJwtTimestampMax) || (nbf_timestamp < 0)) {
587     if (!error_.has_value()) {
588       error_ = util::Status(absl::StatusCode::kInvalidArgument,
589                             "invalid not_before timestamp");
590     }
591     return *this;
592   }
593   auto fields = json_proto_.mutable_fields();
594   Value value;
595   value.set_number_value(nbf_timestamp);
596   (*fields)[std::string(kJwtClaimNotBefore)] = value;
597   return *this;
598 }
599 
SetIssuedAt(absl::Time issued_at)600 RawJwtBuilder& RawJwtBuilder::SetIssuedAt(absl::Time issued_at) {
601   int64_t iat_timestamp = TimeToTimestamp(issued_at);
602   if ((iat_timestamp > kJwtTimestampMax) || (iat_timestamp < 0)) {
603     if (!error_.has_value()) {
604       error_ = util::Status(absl::StatusCode::kInvalidArgument,
605                             "invalid issued_at timestamp");
606     }
607     return *this;
608   }
609   auto fields = json_proto_.mutable_fields();
610   Value value;
611   value.set_number_value(iat_timestamp);
612   (*fields)[std::string(kJwtClaimIssuedAt)] = value;
613   return *this;
614 }
615 
AddNullClaim(absl::string_view name)616 RawJwtBuilder& RawJwtBuilder::AddNullClaim(absl::string_view name) {
617   util::Status status = ValidatePayloadName(name);
618   if (!status.ok()) {
619     if (!error_.has_value()) {
620       error_ = status;
621     }
622     return *this;
623   }
624   auto fields = json_proto_.mutable_fields();
625   Value value;
626   value.set_null_value(google::protobuf::NULL_VALUE);
627   (*fields)[std::string(name)] = value;
628   return *this;
629 }
630 
AddBooleanClaim(absl::string_view name,bool bool_value)631 RawJwtBuilder& RawJwtBuilder::AddBooleanClaim(absl::string_view name,
632                                               bool bool_value) {
633   util::Status status = ValidatePayloadName(name);
634   if (!status.ok()) {
635     if (!error_.has_value()) {
636       error_ = status;
637     }
638     return *this;
639   }
640   auto fields = json_proto_.mutable_fields();
641   Value value;
642   value.set_bool_value(bool_value);
643   (*fields)[std::string(name)] = value;
644   return *this;
645 }
646 
AddStringClaim(absl::string_view name,absl::string_view string_value)647 RawJwtBuilder& RawJwtBuilder::AddStringClaim(absl::string_view name,
648                                              absl::string_view string_value) {
649   util::Status status = ValidatePayloadName(name);
650   if (!status.ok()) {
651     if (!error_.has_value()) {
652       error_ = status;
653     }
654     return *this;
655   }
656   auto fields = json_proto_.mutable_fields();
657   Value value;
658   value.set_string_value(std::string(string_value));
659   (*fields)[std::string(name)] = value;
660   return *this;
661 }
662 
AddNumberClaim(absl::string_view name,double double_value)663 RawJwtBuilder& RawJwtBuilder::AddNumberClaim(absl::string_view name,
664                                              double double_value) {
665   util::Status status = ValidatePayloadName(name);
666   if (!status.ok()) {
667     if (!error_.has_value()) {
668       error_ = status;
669     }
670     return *this;
671   }
672   auto fields = json_proto_.mutable_fields();
673   Value value;
674   value.set_number_value(double_value);
675   (*fields)[std::string(name)] = value;
676   return *this;
677 }
678 
AddJsonObjectClaim(absl::string_view name,absl::string_view object_value)679 RawJwtBuilder& RawJwtBuilder::AddJsonObjectClaim(
680     absl::string_view name, absl::string_view object_value) {
681   util::Status status = ValidatePayloadName(name);
682   if (!status.ok()) {
683     if (!error_.has_value()) {
684       error_ = status;
685     }
686     return *this;
687   }
688   util::StatusOr<google::protobuf::Struct> proto =
689       jwt_internal::JsonStringToProtoStruct(object_value);
690   if (!proto.ok()) {
691     if (!error_.has_value()) {
692       error_ = proto.status();
693     }
694     return *this;
695   }
696   auto fields = json_proto_.mutable_fields();
697   Value value;
698   *value.mutable_struct_value() = *std::move(proto);
699   (*fields)[std::string(name)] = value;
700   return *this;
701 }
702 
AddJsonArrayClaim(absl::string_view name,absl::string_view array_value)703 RawJwtBuilder& RawJwtBuilder::AddJsonArrayClaim(absl::string_view name,
704                                                 absl::string_view array_value) {
705   util::Status status = ValidatePayloadName(name);
706   if (!status.ok()) {
707     if (!error_.has_value()) {
708       error_ = status;
709     }
710     return *this;
711   }
712   util::StatusOr<google::protobuf::ListValue> list =
713       jwt_internal::JsonStringToProtoList(array_value);
714   if (!list.ok()) {
715     if (!error_.has_value()) {
716       error_ = list.status();
717     }
718     return *this;
719   }
720   auto fields = json_proto_.mutable_fields();
721   Value value;
722   *value.mutable_list_value() = *list;
723   (*fields)[std::string(name)] = value;
724   return *this;
725 }
726 
Build()727 util::StatusOr<RawJwt> RawJwtBuilder::Build() {
728   if (error_.has_value()) {
729     return *error_;
730   }
731   if (!json_proto_.fields().contains(std::string(kJwtClaimExpiration)) &&
732       !without_expiration_) {
733     return util::Status(
734         absl::StatusCode::kInvalidArgument,
735         "neither SetExpiration() nor WithoutExpiration() was called");
736   }
737   if (json_proto_.fields().contains(std::string(kJwtClaimExpiration)) &&
738       without_expiration_) {
739     return util::Status(
740         absl::StatusCode::kInvalidArgument,
741         "SetExpiration() and WithoutExpiration() must not be called together");
742   }
743   RawJwt token(type_header_, json_proto_);
744   return token;
745 }
746 
747 }  // namespace tink
748 }  // namespace crypto
749