• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2016 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 //
17 
18 #include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
19 
20 #include <grpc/credentials.h>
21 #include <grpc/support/alloc.h>
22 #include <grpc/support/json.h>
23 #include <grpc/support/port_platform.h>
24 #include <grpc/support/string_util.h>
25 #include <grpc/support/sync.h>
26 #include <inttypes.h>
27 #include <stdlib.h>
28 
29 #include <memory>
30 #include <string>
31 #include <utility>
32 
33 #include "absl/log/check.h"
34 #include "absl/log/log.h"
35 #include "absl/status/status.h"
36 #include "absl/strings/str_cat.h"
37 #include "src/core/lib/debug/trace.h"
38 #include "src/core/lib/iomgr/exec_ctx.h"
39 #include "src/core/lib/promise/promise.h"
40 #include "src/core/lib/security/credentials/call_creds_util.h"
41 #include "src/core/lib/transport/metadata_batch.h"
42 #include "src/core/util/json/json.h"
43 #include "src/core/util/json/json_reader.h"
44 #include "src/core/util/json/json_writer.h"
45 #include "src/core/util/ref_counted_ptr.h"
46 #include "src/core/util/uri.h"
47 
48 using grpc_core::Json;
49 
50 grpc_service_account_jwt_access_credentials::
~grpc_service_account_jwt_access_credentials()51     ~grpc_service_account_jwt_access_credentials() {
52   grpc_auth_json_key_destruct(&key_);
53   gpr_mu_destroy(&cache_mu_);
54 }
55 
56 grpc_core::ArenaPromise<absl::StatusOr<grpc_core::ClientMetadataHandle>>
GetRequestMetadata(grpc_core::ClientMetadataHandle initial_metadata,const grpc_call_credentials::GetRequestMetadataArgs * args)57 grpc_service_account_jwt_access_credentials::GetRequestMetadata(
58     grpc_core::ClientMetadataHandle initial_metadata,
59     const grpc_call_credentials::GetRequestMetadataArgs* args) {
60   gpr_timespec refresh_threshold = gpr_time_from_seconds(
61       GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
62 
63   // Remove service name from service_url to follow the audience format
64   // dictated in https://google.aip.dev/auth/4111.
65   absl::StatusOr<std::string> uri = grpc_core::RemoveServiceNameFromJwtUri(
66       grpc_core::MakeJwtServiceUrl(initial_metadata, args));
67   if (!uri.ok()) {
68     return grpc_core::Immediate(uri.status());
69   }
70   // See if we can return a cached jwt.
71   absl::optional<grpc_core::Slice> jwt_value;
72   {
73     gpr_mu_lock(&cache_mu_);
74     if (cached_.has_value() && cached_->service_url == *uri &&
75         (gpr_time_cmp(
76              gpr_time_sub(cached_->jwt_expiration, gpr_now(GPR_CLOCK_REALTIME)),
77              refresh_threshold) > 0)) {
78       jwt_value = cached_->jwt_value.Ref();
79     }
80     gpr_mu_unlock(&cache_mu_);
81   }
82 
83   if (!jwt_value.has_value()) {
84     char* jwt = nullptr;
85     // Generate a new jwt.
86     gpr_mu_lock(&cache_mu_);
87     cached_.reset();
88     jwt = grpc_jwt_encode_and_sign(&key_, uri->c_str(), jwt_lifetime_, nullptr);
89     if (jwt != nullptr) {
90       std::string md_value = absl::StrCat("Bearer ", jwt);
91       gpr_free(jwt);
92       jwt_value = grpc_core::Slice::FromCopiedString(md_value);
93       cached_ = {jwt_value->Ref(), std::move(*uri),
94                  gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), jwt_lifetime_)};
95     }
96     gpr_mu_unlock(&cache_mu_);
97   }
98 
99   if (!jwt_value.has_value()) {
100     return grpc_core::Immediate(
101         absl::UnauthenticatedError("Could not generate JWT."));
102   }
103 
104   initial_metadata->Append(
105       GRPC_AUTHORIZATION_METADATA_KEY, std::move(*jwt_value),
106       [](absl::string_view, const grpc_core::Slice&) { abort(); });
107   return grpc_core::Immediate(std::move(initial_metadata));
108 }
109 
110 grpc_service_account_jwt_access_credentials::
grpc_service_account_jwt_access_credentials(grpc_auth_json_key key,gpr_timespec token_lifetime)111     grpc_service_account_jwt_access_credentials(grpc_auth_json_key key,
112                                                 gpr_timespec token_lifetime)
113     : key_(key) {
114   gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime();
115   if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) {
116     VLOG(2) << "Cropping token lifetime to maximum allowed value ("
117             << max_token_lifetime.tv_sec << " secs).";
118     token_lifetime = grpc_max_auth_token_lifetime();
119   }
120   jwt_lifetime_ = token_lifetime;
121   gpr_mu_init(&cache_mu_);
122 }
123 
Type()124 grpc_core::UniqueTypeName grpc_service_account_jwt_access_credentials::Type() {
125   static grpc_core::UniqueTypeName::Factory kFactory("Jwt");
126   return kFactory.Create();
127 }
128 
129 grpc_core::RefCountedPtr<grpc_call_credentials>
grpc_service_account_jwt_access_credentials_create_from_auth_json_key(grpc_auth_json_key key,gpr_timespec token_lifetime)130 grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
131     grpc_auth_json_key key, gpr_timespec token_lifetime) {
132   if (!grpc_auth_json_key_is_valid(&key)) {
133     LOG(ERROR) << "Invalid input for jwt credentials creation";
134     return nullptr;
135   }
136   return grpc_core::MakeRefCounted<grpc_service_account_jwt_access_credentials>(
137       key, token_lifetime);
138 }
139 
redact_private_key(const char * json_key)140 static char* redact_private_key(const char* json_key) {
141   auto json = grpc_core::JsonParse(json_key);
142   if (!json.ok() || json->type() != Json::Type::kObject) {
143     return gpr_strdup("<Json failed to parse.>");
144   }
145   Json::Object object = json->object();
146   object["private_key"] = Json::FromString("<redacted>");
147   return gpr_strdup(
148       grpc_core::JsonDump(Json::FromObject(std::move(object)), /*indent=*/2)
149           .c_str());
150 }
151 
grpc_service_account_jwt_access_credentials_create(const char * json_key,gpr_timespec token_lifetime,void * reserved)152 grpc_call_credentials* grpc_service_account_jwt_access_credentials_create(
153     const char* json_key, gpr_timespec token_lifetime, void* reserved) {
154   if (GRPC_TRACE_FLAG_ENABLED(api)) {
155     char* clean_json = redact_private_key(json_key);
156     VLOG(2) << "grpc_service_account_jwt_access_credentials_create("
157             << "json_key=" << clean_json
158             << ", token_lifetime=gpr_timespec { tv_sec: "
159             << token_lifetime.tv_sec << ", tv_nsec: " << token_lifetime.tv_nsec
160             << ", clock_type: " << token_lifetime.clock_type
161             << " }, reserved=" << reserved << ")";
162     gpr_free(clean_json);
163   }
164   CHECK_EQ(reserved, nullptr);
165   grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
166   grpc_core::ExecCtx exec_ctx;
167   return grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
168              grpc_auth_json_key_create_from_string(json_key), token_lifetime)
169       .release();
170 }
171 
172 namespace grpc_core {
173 
RemoveServiceNameFromJwtUri(absl::string_view uri)174 absl::StatusOr<std::string> RemoveServiceNameFromJwtUri(absl::string_view uri) {
175   auto parsed = URI::Parse(uri);
176   if (!parsed.ok()) {
177     return parsed.status();
178   }
179   return absl::StrFormat("%s://%s/", parsed->scheme(), parsed->authority());
180 }
181 
182 }  // namespace grpc_core
183