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