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