Merge branch 'master' of github.com:grpc/grpc into cpp_auth_md_processor
diff --git a/BUILD b/BUILD
index d612d9c..bc2136d 100644
--- a/BUILD
+++ b/BUILD
@@ -719,6 +719,7 @@
     "src/cpp/util/time.cc",
   ],
   hdrs = [
+    "include/grpc++/auth_metadata_processor.h",
     "include/grpc++/channel.h",
     "include/grpc++/client_context.h",
     "include/grpc++/completion_queue.h",
@@ -808,6 +809,7 @@
     "src/cpp/util/time.cc",
   ],
   hdrs = [
+    "include/grpc++/auth_metadata_processor.h",
     "include/grpc++/channel.h",
     "include/grpc++/client_context.h",
     "include/grpc++/completion_queue.h",
diff --git a/Makefile b/Makefile
index 135d38c..4c4a5b2 100644
--- a/Makefile
+++ b/Makefile
@@ -4583,6 +4583,7 @@
     src/cpp/util/time.cc \
 
 PUBLIC_HEADERS_CXX += \
+    include/grpc++/auth_metadata_processor.h \
     include/grpc++/channel.h \
     include/grpc++/client_context.h \
     include/grpc++/completion_queue.h \
@@ -4827,6 +4828,7 @@
     src/cpp/util/time.cc \
 
 PUBLIC_HEADERS_CXX += \
+    include/grpc++/auth_metadata_processor.h \
     include/grpc++/channel.h \
     include/grpc++/client_context.h \
     include/grpc++/completion_queue.h \
diff --git a/build.json b/build.json
index a451fc3..daa0178 100644
--- a/build.json
+++ b/build.json
@@ -30,6 +30,7 @@
     {
       "name": "grpc++_base",
       "public_headers": [
+        "include/grpc++/auth_metadata_processor.h",
         "include/grpc++/channel.h",
         "include/grpc++/client_context.h",
         "include/grpc++/completion_queue.h",
diff --git a/include/grpc++/auth_metadata_processor.h b/include/grpc++/auth_metadata_processor.h
new file mode 100644
index 0000000..a42abef
--- /dev/null
+++ b/include/grpc++/auth_metadata_processor.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPCXX_AUTH_METADATA_PROCESSOR_H_
+#define GRPCXX_AUTH_METADATA_PROCESSOR_H_
+
+#include <map>
+
+#include <grpc++/support/auth_context.h>
+#include <grpc++/support/status.h>
+#include <grpc++/support/string_ref.h>
+
+namespace grpc {
+
+class AuthMetadataProcessor {
+ public:
+  typedef std::multimap<grpc::string_ref, grpc::string_ref> InputMetadata;
+  typedef std::multimap<grpc::string, grpc::string_ref> OutputMetadata;
+
+  virtual ~AuthMetadataProcessor() {}
+
+  // If this method returns true, the Process function will be scheduled in
+  // a different thread from the one processing the call.
+  virtual bool IsBlocking() const { return true; }
+
+  // context is read/write: it contains the properties of the channel peer and
+  // it is the job of the Process method to augment it with properties derived
+  // from the passed-in auth_metadata.
+  // consumed_auth_metadata needs to be filled with metadata that has been
+  // consumed by the processor and will be removed from the call.
+  // response_metadata is the metadata that will be sent as part of the
+  // response.
+  // If the return value is not Status::OK, the rpc call will be aborted with
+  // the error code and error message sent back to the client.
+  virtual Status Process(const InputMetadata& auth_metadata,
+                         AuthContext* context,
+                         OutputMetadata* consumed_auth_metadata,
+                         OutputMetadata* response_metadata) = 0;
+};
+
+}  // namespace grpc
+
+#endif  // GRPCXX_AUTH_METADATA_PROCESSOR_H_
+
diff --git a/include/grpc++/server_credentials.h b/include/grpc++/server_credentials.h
index 16b78c0..e006f3a 100644
--- a/include/grpc++/server_credentials.h
+++ b/include/grpc++/server_credentials.h
@@ -38,6 +38,7 @@
 #include <vector>
 
 #include <grpc++/support/config.h>
+#include <grpc++/auth_metadata_processor.h>
 
 struct grpc_server;
 
@@ -49,6 +50,11 @@
  public:
   virtual ~ServerCredentials();
 
+  // This method is not thread-safe and has to be called before the server is
+  // started. The last call to this function wins.
+  virtual void SetAuthMetadataProcessor(
+      const std::shared_ptr<AuthMetadataProcessor>& processor) = 0;
+
  private:
   friend class ::grpc::Server;
 
diff --git a/include/grpc++/support/auth_context.h b/include/grpc++/support/auth_context.h
index 67e3e66..fc2701e 100644
--- a/include/grpc++/support/auth_context.h
+++ b/include/grpc++/support/auth_context.h
@@ -77,6 +77,9 @@
  public:
   virtual ~AuthContext() {}
 
+  // Returns true if the peer is authenticated.
+  virtual bool IsPeerAuthenticated() const = 0;
+
   // A peer identity, in general is one or more properties (in which case they
   // have the same name).
   virtual std::vector<grpc::string_ref> GetPeerIdentity() const = 0;
@@ -89,6 +92,11 @@
   // Iteration over all the properties.
   virtual AuthPropertyIterator begin() const = 0;
   virtual AuthPropertyIterator end() const = 0;
+
+  // Mutation functions: should only be used by an AuthMetadataProcessor.
+  virtual void AddProperty(const grpc::string& key,
+                           const grpc::string_ref& value) = 0;
+  virtual bool SetPeerIdentityPropertyName(const grpc::string& name) = 0;
 };
 
 }  // namespace grpc
diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h
index 049ab3c..87bc250 100644
--- a/include/grpc/grpc_security.h
+++ b/include/grpc/grpc_security.h
@@ -275,10 +275,12 @@
 typedef struct {
   /* The context object is read/write: it contains the properties of the
      channel peer and it is the job of the process function to augment it with
-     properties derived from the passed-in metadata. */
+     properties derived from the passed-in metadata.
+     The lifetime of these objects is guaranteed until cb is invoked. */
   void (*process)(void *state, grpc_auth_context *context,
-                  const grpc_metadata *md, size_t md_count,
+                  const grpc_metadata *md, size_t num_md,
                   grpc_process_auth_metadata_done_cb cb, void *user_data);
+  void (*destroy)(void *state);
   void *state;
 } grpc_auth_metadata_processor;
 
diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c
index 1c665f1..721e871 100644
--- a/src/core/security/credentials.c
+++ b/src/core/security/credentials.c
@@ -152,9 +152,20 @@
 void grpc_server_credentials_set_auth_metadata_processor(
     grpc_server_credentials *creds, grpc_auth_metadata_processor processor) {
   if (creds == NULL) return;
+  if (creds->processor.destroy != NULL && creds->processor.state != NULL) {
+    creds->processor.destroy(creds->processor.state);
+  }
   creds->processor = processor;
 }
 
+void grpc_server_credentials_destroy(grpc_server_credentials *creds) {
+  if (creds == NULL) return;
+  if (creds->processor.destroy != NULL && creds->processor.state != NULL) {
+    creds->processor.destroy(creds->processor.state);
+  }
+  gpr_free(creds);
+}
+
 /* -- Ssl credentials. -- */
 
 static void ssl_destroy(grpc_credentials *creds) {
@@ -185,7 +196,7 @@
     gpr_free(c->config.pem_cert_chains_sizes);
   }
   if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
-  gpr_free(creds);
+  grpc_server_credentials_destroy(creds);
 }
 
 static int ssl_has_request_metadata(const grpc_credentials *creds) { return 0; }
@@ -834,7 +845,7 @@
 
 static void fake_transport_security_server_credentials_destroy(
     grpc_server_credentials *creds) {
-  gpr_free(creds);
+  grpc_server_credentials_destroy(creds);
 }
 
 static int fake_transport_security_has_request_metadata(
diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c
index c1b434f..95d80ba 100644
--- a/src/core/security/security_context.c
+++ b/src/core/security/security_context.c
@@ -42,19 +42,6 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-/* --- grpc_process_auth_metadata_func --- */
-
-static grpc_auth_metadata_processor server_processor = {NULL, NULL};
-
-grpc_auth_metadata_processor grpc_server_get_auth_metadata_processor(void) {
-  return server_processor;
-}
-
-void grpc_server_register_auth_metadata_processor(
-    grpc_auth_metadata_processor processor) {
-  server_processor = processor;
-}
-
 /* --- grpc_call --- */
 
 grpc_call_error grpc_call_set_credentials(grpc_call *call,
diff --git a/src/core/security/server_auth_filter.c b/src/core/security/server_auth_filter.c
index 6e83143..b767f85 100644
--- a/src/core/security/server_auth_filter.c
+++ b/src/core/security/server_auth_filter.c
@@ -50,6 +50,7 @@
      handling it. */
   grpc_iomgr_closure auth_on_recv;
   grpc_transport_stream_op transport_op;
+  grpc_metadata_array md;
   const grpc_metadata *consumed_md;
   size_t num_consumed_md;
   grpc_stream_op *md_op;
@@ -90,13 +91,17 @@
   call_data *calld = elem->call_data;
   size_t i;
   for (i = 0; i < calld->num_consumed_md; i++) {
+    const grpc_metadata *consumed_md = &calld->consumed_md[i];
     /* Maybe we could do a pointer comparison but we do not have any guarantee
        that the metadata processor used the same pointers for consumed_md in the
        callback. */
-    if (memcmp(GPR_SLICE_START_PTR(md->key->slice), calld->consumed_md[i].key,
+    if (GPR_SLICE_LENGTH(md->key->slice) != strlen(consumed_md->key) ||
+        GPR_SLICE_LENGTH(md->value->slice) != consumed_md->value_length) {
+      continue;
+    }
+    if (memcmp(GPR_SLICE_START_PTR(md->key->slice), consumed_md->key,
                GPR_SLICE_LENGTH(md->key->slice)) == 0 &&
-        memcmp(GPR_SLICE_START_PTR(md->value->slice),
-               calld->consumed_md[i].value,
+        memcmp(GPR_SLICE_START_PTR(md->value->slice), consumed_md->value,
                GPR_SLICE_LENGTH(md->value->slice)) == 0) {
       return NULL; /* Delete. */
     }
@@ -134,6 +139,7 @@
     grpc_transport_stream_op_add_close(&calld->transport_op, status, &message);
     grpc_call_next_op(elem, &calld->transport_op);
   }
+  grpc_metadata_array_destroy(&calld->md);
 }
 
 static void auth_on_recv(void *user_data, int success) {
@@ -145,17 +151,15 @@
     size_t nops = calld->recv_ops->nops;
     grpc_stream_op *ops = calld->recv_ops->ops;
     for (i = 0; i < nops; i++) {
-      grpc_metadata_array md_array;
       grpc_stream_op *op = &ops[i];
       if (op->type != GRPC_OP_METADATA || calld->got_client_metadata) continue;
       calld->got_client_metadata = 1;
       if (chand->processor.process == NULL) continue;
       calld->md_op = op;
-      md_array = metadata_batch_to_md_array(&op->data.metadata);
+      calld->md = metadata_batch_to_md_array(&op->data.metadata);
       chand->processor.process(chand->processor.state, calld->auth_context,
-                               md_array.metadata, md_array.count,
+                               calld->md.metadata, calld->md.count,
                                on_md_processing_done, elem);
-      grpc_metadata_array_destroy(&md_array);
       return;
     }
   }
diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c
index 8d9d036..96ca4cb 100644
--- a/src/core/security/server_secure_chttp2.c
+++ b/src/core/security/server_secure_chttp2.c
@@ -79,6 +79,9 @@
     gpr_mu_unlock(&state->mu);
     /* clean up */
     GRPC_SECURITY_CONNECTOR_UNREF(state->sc, "server");
+    if (state->processor.state != NULL && state->processor.destroy != NULL) {
+      state->processor.destroy(state->processor.state);
+    }
     gpr_free(state);
   }
 }
@@ -262,7 +265,13 @@
   state->server = server;
   state->tcp = tcp;
   state->sc = sc;
+
+  /* Transfer ownership of the processor. */
   state->processor = creds->processor;
+  creds->processor.state = NULL;
+  creds->processor.destroy =  NULL;
+  creds->processor.process = NULL;
+
   state->handshaking_tcp_endpoints = NULL;
   state->is_shutdown = 0;
   gpr_mu_init(&state->mu);
diff --git a/src/cpp/client/create_channel.cc b/src/cpp/client/create_channel.cc
index 1dac960..d2b2d30 100644
--- a/src/cpp/client/create_channel.cc
+++ b/src/cpp/client/create_channel.cc
@@ -51,6 +51,7 @@
 std::shared_ptr<Channel> CreateCustomChannel(
     const grpc::string& target, const std::shared_ptr<Credentials>& creds,
     const ChannelArguments& args) {
+  GrpcLibrary init_lib;  // We need to call init in case of a bad creds.
   ChannelArguments cp_args = args;
   std::ostringstream user_agent_prefix;
   user_agent_prefix << "grpc-c++/" << grpc_version_string();
diff --git a/src/cpp/common/secure_auth_context.cc b/src/cpp/common/secure_auth_context.cc
index b18a853..8615ac8 100644
--- a/src/cpp/common/secure_auth_context.cc
+++ b/src/cpp/common/secure_auth_context.cc
@@ -37,9 +37,13 @@
 
 namespace grpc {
 
-SecureAuthContext::SecureAuthContext(grpc_auth_context* ctx) : ctx_(ctx) {}
+SecureAuthContext::SecureAuthContext(grpc_auth_context* ctx,
+                                     bool take_ownership)
+    : ctx_(ctx), take_ownership_(take_ownership) {}
 
-SecureAuthContext::~SecureAuthContext() { grpc_auth_context_release(ctx_); }
+SecureAuthContext::~SecureAuthContext() {
+  if (take_ownership_) grpc_auth_context_release(ctx_);
+}
 
 std::vector<grpc::string_ref> SecureAuthContext::GetPeerIdentity() const {
   if (!ctx_) {
@@ -94,4 +98,21 @@
   return AuthPropertyIterator();
 }
 
+void SecureAuthContext::AddProperty(const grpc::string& key,
+                                    const grpc::string_ref& value) {
+  if (!ctx_) return;
+  grpc_auth_context_add_property(ctx_, key.c_str(), value.data(), value.size());
+}
+
+bool SecureAuthContext::SetPeerIdentityPropertyName(const grpc::string& name) {
+  if (!ctx_) return false;
+  return grpc_auth_context_set_peer_identity_property_name(ctx_,
+                                                           name.c_str()) != 0;
+}
+
+bool SecureAuthContext::IsPeerAuthenticated() const {
+  if (!ctx_) return false;
+  return grpc_auth_context_peer_is_authenticated(ctx_) != 0;
+}
+
 }  // namespace grpc
diff --git a/src/cpp/common/secure_auth_context.h b/src/cpp/common/secure_auth_context.h
index 7f622b8..6edab0c 100644
--- a/src/cpp/common/secure_auth_context.h
+++ b/src/cpp/common/secure_auth_context.h
@@ -42,10 +42,12 @@
 
 class SecureAuthContext GRPC_FINAL : public AuthContext {
  public:
-  SecureAuthContext(grpc_auth_context* ctx);
+  SecureAuthContext(grpc_auth_context* ctx, bool take_ownership);
 
   ~SecureAuthContext() GRPC_OVERRIDE;
 
+  bool IsPeerAuthenticated() const GRPC_OVERRIDE;
+
   std::vector<grpc::string_ref> GetPeerIdentity() const GRPC_OVERRIDE;
 
   grpc::string GetPeerIdentityPropertyName() const GRPC_OVERRIDE;
@@ -57,8 +59,15 @@
 
   AuthPropertyIterator end() const GRPC_OVERRIDE;
 
+  void AddProperty(const grpc::string& key,
+                   const grpc::string_ref& value) GRPC_OVERRIDE;
+
+  virtual bool SetPeerIdentityPropertyName(const grpc::string& name)
+      GRPC_OVERRIDE;
+
  private:
   grpc_auth_context* ctx_;
+  bool take_ownership_;
 };
 
 }  // namespace grpc
diff --git a/src/cpp/common/secure_create_auth_context.cc b/src/cpp/common/secure_create_auth_context.cc
index f13d25a..5a907d0 100644
--- a/src/cpp/common/secure_create_auth_context.cc
+++ b/src/cpp/common/secure_create_auth_context.cc
@@ -44,7 +44,7 @@
     return std::shared_ptr<const AuthContext>();
   }
   return std::shared_ptr<const AuthContext>(
-      new SecureAuthContext(grpc_call_auth_context(call)));
+      new SecureAuthContext(grpc_call_auth_context(call), true));
 }
 
 }  // namespace grpc
diff --git a/src/cpp/server/insecure_server_credentials.cc b/src/cpp/server/insecure_server_credentials.cc
index 800cd36..12807e7 100644
--- a/src/cpp/server/insecure_server_credentials.cc
+++ b/src/cpp/server/insecure_server_credentials.cc
@@ -34,6 +34,7 @@
 #include <grpc++/server_credentials.h>
 
 #include <grpc/grpc.h>
+#include <grpc/support/log.h>
 
 namespace grpc {
 namespace {
@@ -43,6 +44,11 @@
                       grpc_server* server) GRPC_OVERRIDE {
     return grpc_server_add_insecure_http2_port(server, addr.c_str());
   }
+  void SetAuthMetadataProcessor(
+      const std::shared_ptr<AuthMetadataProcessor>& processor) GRPC_OVERRIDE {
+    (void)processor;
+    GPR_ASSERT(0);  // Should not be called on InsecureServerCredentials.
+  }
 };
 }  // namespace
 
diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc
index 5bce9ca..0e2927b 100644
--- a/src/cpp/server/secure_server_credentials.cc
+++ b/src/cpp/server/secure_server_credentials.cc
@@ -31,15 +31,94 @@
  *
  */
 
+#include <functional>
+#include <map>
+#include <memory>
+
+
+#include "src/cpp/common/secure_auth_context.h"
 #include "src/cpp/server/secure_server_credentials.h"
 
+#include <grpc++/auth_metadata_processor.h>
+
 namespace grpc {
 
+void AuthMetadataProcessorAyncWrapper::Destroy(void *wrapper) {
+  auto* w = reinterpret_cast<AuthMetadataProcessorAyncWrapper*>(wrapper);
+  delete w;
+}
+
+void AuthMetadataProcessorAyncWrapper::Process(
+    void* wrapper, grpc_auth_context* context, const grpc_metadata* md,
+    size_t num_md, grpc_process_auth_metadata_done_cb cb, void* user_data) {
+  auto* w = reinterpret_cast<AuthMetadataProcessorAyncWrapper*>(wrapper);
+  if (w->processor_ == nullptr) {
+    // Early exit.
+    cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_OK, nullptr);
+    return;
+  }
+  if (w->processor_->IsBlocking()) {
+    w->thread_pool_->Add(
+        std::bind(&AuthMetadataProcessorAyncWrapper::InvokeProcessor, w,
+                  context, md, num_md, cb, user_data));
+  } else {
+    // invoke directly.
+    w->InvokeProcessor(context, md, num_md, cb, user_data);
+  }
+}
+
+void AuthMetadataProcessorAyncWrapper::InvokeProcessor(
+    grpc_auth_context* ctx,
+    const grpc_metadata* md, size_t num_md,
+    grpc_process_auth_metadata_done_cb cb, void* user_data) {
+  AuthMetadataProcessor::InputMetadata metadata;
+  for (size_t i = 0; i < num_md; i++) {
+    metadata.insert(std::make_pair(
+        md[i].key, grpc::string_ref(md[i].value, md[i].value_length)));
+  }
+  SecureAuthContext context(ctx, false);
+  AuthMetadataProcessor::OutputMetadata consumed_metadata;
+  AuthMetadataProcessor::OutputMetadata response_metadata;
+
+  Status status = processor_->Process(metadata, &context, &consumed_metadata,
+                                      &response_metadata);
+
+  std::vector<grpc_metadata> consumed_md;
+  for (auto it = consumed_metadata.begin(); it != consumed_metadata.end();
+       ++it) {
+    consumed_md.push_back({it->first.c_str(),
+                           it->second.data(),
+                           it->second.size(),
+                           0,
+                           {{nullptr, nullptr, nullptr, nullptr}}});
+  }
+  std::vector<grpc_metadata> response_md;
+  for (auto it = response_metadata.begin(); it != response_metadata.end();
+       ++it) {
+    response_md.push_back({it->first.c_str(),
+                           it->second.data(),
+                           it->second.size(),
+                           0,
+                           {{nullptr, nullptr, nullptr, nullptr}}});
+  }
+  cb(user_data, &consumed_md[0], consumed_md.size(), &response_md[0],
+     response_md.size(), static_cast<grpc_status_code>(status.error_code()),
+     status.error_message().c_str());
+}
+
 int SecureServerCredentials::AddPortToServer(const grpc::string& addr,
                                              grpc_server* server) {
   return grpc_server_add_secure_http2_port(server, addr.c_str(), creds_);
 }
 
+void SecureServerCredentials::SetAuthMetadataProcessor(
+    const std::shared_ptr<AuthMetadataProcessor>& processor) {
+  auto *wrapper = new AuthMetadataProcessorAyncWrapper(processor);
+  grpc_server_credentials_set_auth_metadata_processor(
+      creds_, {AuthMetadataProcessorAyncWrapper::Process,
+               AuthMetadataProcessorAyncWrapper::Destroy, wrapper});
+}
+
 std::shared_ptr<ServerCredentials> SslServerCredentials(
     const SslServerCredentialsOptions& options) {
   std::vector<grpc_ssl_pem_key_cert_pair> pem_key_cert_pairs;
diff --git a/src/cpp/server/secure_server_credentials.h b/src/cpp/server/secure_server_credentials.h
index d3d37b1..17be9e3 100644
--- a/src/cpp/server/secure_server_credentials.h
+++ b/src/cpp/server/secure_server_credentials.h
@@ -34,12 +34,36 @@
 #ifndef GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
 #define GRPC_INTERNAL_CPP_SERVER_SECURE_SERVER_CREDENTIALS_H
 
+#include <memory>
+
 #include <grpc++/server_credentials.h>
 
 #include <grpc/grpc_security.h>
 
+#include "src/cpp/server/thread_pool_interface.h"
+
 namespace grpc {
 
+class AuthMetadataProcessorAyncWrapper GRPC_FINAL {
+ public:
+  static void Destroy(void *wrapper);
+
+  static void Process(void* wrapper, grpc_auth_context* context,
+                      const grpc_metadata* md, size_t num_md,
+                      grpc_process_auth_metadata_done_cb cb, void* user_data);
+
+  AuthMetadataProcessorAyncWrapper(
+      const std::shared_ptr<AuthMetadataProcessor>& processor)
+      : thread_pool_(CreateDefaultThreadPool()), processor_(processor) {}
+
+ private:
+  void InvokeProcessor(grpc_auth_context* context, const grpc_metadata* md,
+                       size_t num_md, grpc_process_auth_metadata_done_cb cb,
+                       void* user_data);
+  std::unique_ptr<ThreadPoolInterface> thread_pool_;
+  std::shared_ptr<AuthMetadataProcessor> processor_;
+};
+
 class SecureServerCredentials GRPC_FINAL : public ServerCredentials {
  public:
   explicit SecureServerCredentials(grpc_server_credentials* creds)
@@ -51,8 +75,12 @@
   int AddPortToServer(const grpc::string& addr,
                       grpc_server* server) GRPC_OVERRIDE;
 
+  void SetAuthMetadataProcessor(
+      const std::shared_ptr<AuthMetadataProcessor>& processor) GRPC_OVERRIDE;
+
  private:
-  grpc_server_credentials* const creds_;
+  grpc_server_credentials* creds_;
+  std::unique_ptr<AuthMetadataProcessorAyncWrapper> processor_;
 };
 
 }  // namespace grpc
diff --git a/test/core/end2end/fixtures/chttp2_fake_security.c b/test/core/end2end/fixtures/chttp2_fake_security.c
index b4a248f..3e64cc0 100644
--- a/test/core/end2end/fixtures/chttp2_fake_security.c
+++ b/test/core/end2end/fixtures/chttp2_fake_security.c
@@ -128,7 +128,7 @@
   grpc_server_credentials *fake_ts_creds =
       grpc_fake_transport_security_server_credentials_create();
   if (fail_server_auth_check(server_args)) {
-    grpc_auth_metadata_processor processor = {process_auth_failure, NULL};
+    grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL};
     grpc_server_credentials_set_auth_metadata_processor(fake_ts_creds,
                                                         processor);
   }
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
index 201d202..9193a09 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
@@ -138,7 +138,7 @@
   grpc_server_credentials *ssl_creds =
       grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL);
   if (fail_server_auth_check(server_args)) {
-    grpc_auth_metadata_processor processor = {process_auth_failure, NULL};
+    grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL};
     grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor);
   }
   chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c
index e7375f1..2c605d1 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_poll.c
@@ -138,7 +138,7 @@
   grpc_server_credentials *ssl_creds =
       grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL);
   if (fail_server_auth_check(server_args)) {
-    grpc_auth_metadata_processor processor = {process_auth_failure, NULL};
+    grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL};
     grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor);
   }
   chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_proxy.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_proxy.c
index be0dda2..8133a69 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_proxy.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack_with_proxy.c
@@ -167,7 +167,7 @@
   grpc_server_credentials *ssl_creds =
       grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL);
   if (fail_server_auth_check(server_args)) {
-    grpc_auth_metadata_processor processor = {process_auth_failure, NULL};
+    grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL};
     grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor);
   }
   chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
index 9a545b1..e61e276 100644
--- a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
@@ -67,13 +67,21 @@
   return NULL;
 }
 
+typedef struct {
+  size_t pseudo_refcount;
+} test_processor_state;
+
 static void process_oauth2_success(void *state, grpc_auth_context *ctx,
                                    const grpc_metadata *md, size_t md_count,
                                    grpc_process_auth_metadata_done_cb cb,
                                    void *user_data) {
   const grpc_metadata *oauth2 =
       find_metadata(md, md_count, "Authorization", oauth2_md);
-  GPR_ASSERT(state == NULL);
+  test_processor_state *s;
+
+  GPR_ASSERT(state != NULL);
+  s = (test_processor_state *)state;
+  GPR_ASSERT(s->pseudo_refcount == 1);
   GPR_ASSERT(oauth2 != NULL);
   grpc_auth_context_add_cstring_property(ctx, client_identity_property_name,
                                          client_identity);
@@ -88,7 +96,10 @@
                                    void *user_data) {
   const grpc_metadata *oauth2 =
       find_metadata(md, md_count, "Authorization", oauth2_md);
-  GPR_ASSERT(state == NULL);
+  test_processor_state *s;
+  GPR_ASSERT(state != NULL);
+  s = (test_processor_state *)state;
+  GPR_ASSERT(s->pseudo_refcount == 1);
   GPR_ASSERT(oauth2 != NULL);
   cb(user_data, oauth2, 1, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, NULL);
 }
@@ -171,20 +182,34 @@
   return 0;
 }
 
+static void processor_destroy(void *state) {
+  test_processor_state *s = (test_processor_state *)state;
+  GPR_ASSERT((s->pseudo_refcount--) == 1);
+  gpr_free(s);
+}
+
+static grpc_auth_metadata_processor test_processor_create(int failing) {
+  test_processor_state *s = gpr_malloc(sizeof(*s));
+  grpc_auth_metadata_processor result;
+  s->pseudo_refcount = 1;
+  result.state = s;
+  result.destroy = processor_destroy;
+  if (failing) {
+    result.process = process_oauth2_failure;
+  } else {
+    result.process = process_oauth2_success;
+  }
+  return result;
+}
+
 static void chttp2_init_server_simple_ssl_secure_fullstack(
     grpc_end2end_test_fixture *f, grpc_channel_args *server_args) {
   grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {test_server1_key,
                                                   test_server1_cert};
   grpc_server_credentials *ssl_creds =
       grpc_ssl_server_credentials_create(NULL, &pem_key_cert_pair, 1, 0, NULL);
-  grpc_auth_metadata_processor processor;
-  processor.state = NULL;
-  if (fail_server_auth_check(server_args)) {
-    processor.process = process_oauth2_failure;
-  } else {
-    processor.process = process_oauth2_success;
-  }
-  grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor);
+  grpc_server_credentials_set_auth_metadata_processor(
+      ssl_creds, test_processor_create(fail_server_auth_check(server_args)));
   chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
 }
 
diff --git a/test/cpp/common/secure_auth_context_test.cc b/test/cpp/common/secure_auth_context_test.cc
index 25538c1..e1f2c5e 100644
--- a/test/cpp/common/secure_auth_context_test.cc
+++ b/test/cpp/common/secure_auth_context_test.cc
@@ -50,7 +50,7 @@
 
 // Created with nullptr
 TEST_F(SecureAuthContextTest, EmptyContext) {
-  SecureAuthContext context(nullptr);
+  SecureAuthContext context(nullptr, true);
   EXPECT_TRUE(context.GetPeerIdentity().empty());
   EXPECT_TRUE(context.GetPeerIdentityPropertyName().empty());
   EXPECT_TRUE(context.FindPropertyValues("").empty());
@@ -60,12 +60,12 @@
 
 TEST_F(SecureAuthContextTest, Properties) {
   grpc_auth_context* ctx = grpc_auth_context_create(NULL);
-  grpc_auth_context_add_cstring_property(ctx, "name", "chapi");
-  grpc_auth_context_add_cstring_property(ctx, "name", "chapo");
-  grpc_auth_context_add_cstring_property(ctx, "foo", "bar");
-  EXPECT_EQ(1, grpc_auth_context_set_peer_identity_property_name(ctx, "name"));
+  SecureAuthContext context(ctx, true);
+  context.AddProperty("name", "chapi");
+  context.AddProperty("name", "chapo");
+  context.AddProperty("foo", "bar");
+  EXPECT_TRUE(context.SetPeerIdentityPropertyName("name"));
 
-  SecureAuthContext context(ctx);
   std::vector<grpc::string_ref> peer_identity = context.GetPeerIdentity();
   EXPECT_EQ(2u, peer_identity.size());
   EXPECT_EQ("chapi", ToString(peer_identity[0]));
@@ -78,12 +78,12 @@
 
 TEST_F(SecureAuthContextTest, Iterators) {
   grpc_auth_context* ctx = grpc_auth_context_create(NULL);
-  grpc_auth_context_add_cstring_property(ctx, "name", "chapi");
-  grpc_auth_context_add_cstring_property(ctx, "name", "chapo");
-  grpc_auth_context_add_cstring_property(ctx, "foo", "bar");
-  EXPECT_EQ(1, grpc_auth_context_set_peer_identity_property_name(ctx, "name"));
+  SecureAuthContext context(ctx, true);
+  context.AddProperty("name", "chapi");
+  context.AddProperty("name", "chapo");
+  context.AddProperty("foo", "bar");
+  EXPECT_TRUE(context.SetPeerIdentityPropertyName("name"));
 
-  SecureAuthContext context(ctx);
   AuthPropertyIterator iter = context.begin();
   EXPECT_TRUE(context.end() != iter);
   AuthProperty p0 = *iter;
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index 5e2332c..3fc73ab 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -37,6 +37,7 @@
 #include <grpc/grpc.h>
 #include <grpc/support/thd.h>
 #include <grpc/support/time.h>
+#include <grpc++/auth_metadata_processor.h>
 #include <grpc++/channel.h>
 #include <grpc++/client_context.h>
 #include <grpc++/create_channel.h>
@@ -79,14 +80,23 @@
   }
 }
 
-void CheckServerAuthContext(const ServerContext* context) {
+void CheckServerAuthContext(const ServerContext* context,
+                            const grpc::string& expected_client_identity) {
   std::shared_ptr<const AuthContext> auth_ctx = context->auth_context();
   std::vector<grpc::string_ref> ssl =
       auth_ctx->FindPropertyValues("transport_security_type");
   EXPECT_EQ(1u, ssl.size());
   EXPECT_EQ("ssl", ToString(ssl[0]));
-  EXPECT_TRUE(auth_ctx->GetPeerIdentityPropertyName().empty());
-  EXPECT_TRUE(auth_ctx->GetPeerIdentity().empty());
+  if (expected_client_identity.length() == 0) {
+    EXPECT_TRUE(auth_ctx->GetPeerIdentityPropertyName().empty());
+    EXPECT_TRUE(auth_ctx->GetPeerIdentity().empty());
+    EXPECT_FALSE(auth_ctx->IsPeerAuthenticated());
+  } else {
+    auto identity = auth_ctx->GetPeerIdentity();
+    EXPECT_TRUE(auth_ctx->IsPeerAuthenticated());
+    EXPECT_EQ(1u, identity.size());
+    EXPECT_EQ(expected_client_identity, identity[0]);
+  }
 }
 
 bool CheckIsLocalhost(const grpc::string& addr) {
@@ -98,6 +108,54 @@
          addr.substr(0, kIpv6.size()) == kIpv6;
 }
 
+class TestAuthMetadataProcessor : public AuthMetadataProcessor {
+ public:
+  static const char kGoodGuy[];
+
+  TestAuthMetadataProcessor(bool is_blocking) : is_blocking_(is_blocking) {}
+
+  std::shared_ptr<Credentials> GetCompatibleClientCreds() {
+    return AccessTokenCredentials(kGoodGuy);
+  }
+  std::shared_ptr<Credentials> GetIncompatibleClientCreds() {
+    return AccessTokenCredentials("Mr Hyde");
+  }
+
+  // Interface implementation
+  bool IsBlocking() const GRPC_OVERRIDE { return is_blocking_; }
+
+  Status Process(const InputMetadata& auth_metadata, AuthContext* context,
+                 OutputMetadata* consumed_auth_metadata,
+                 OutputMetadata* response_metadata) GRPC_OVERRIDE {
+    EXPECT_TRUE(consumed_auth_metadata != nullptr);
+    EXPECT_TRUE(context != nullptr);
+    EXPECT_TRUE(response_metadata != nullptr);
+    auto auth_md = auth_metadata.find(GRPC_AUTHORIZATION_METADATA_KEY);
+    EXPECT_NE(auth_md, auth_metadata.end());
+    string_ref auth_md_value = auth_md->second;
+    if (auth_md_value.ends_with("Dr Jekyll")) {
+      context->AddProperty(kIdentityPropName, kGoodGuy);
+      context->SetPeerIdentityPropertyName(kIdentityPropName);
+      consumed_auth_metadata->insert(
+          std::make_pair(string(auth_md->first.data(), auth_md->first.length()),
+                         auth_md->second));
+      return Status::OK;
+    } else {
+      return Status(StatusCode::UNAUTHENTICATED,
+                    string("Invalid principal: ") +
+                        string(auth_md_value.data(), auth_md_value.length()));
+    }
+  }
+
+ protected:
+  static const char kIdentityPropName[];
+  bool is_blocking_;
+};
+
+const char TestAuthMetadataProcessor::kGoodGuy[] = "Dr Jekyll";
+const char TestAuthMetadataProcessor::kIdentityPropName[] = "novel identity";
+
+
 }  // namespace
 
 class Proxy : public ::grpc::cpp::test::util::TestService::Service {
@@ -162,8 +220,10 @@
                                      ToString(iter->second));
       }
     }
-    if (request->has_param() && request->param().check_auth_context()) {
-      CheckServerAuthContext(context);
+    if (request->has_param() &&
+        (request->param().expected_client_identity().length() > 0 ||
+         request->param().check_auth_context())) {
+      CheckServerAuthContext(context, request->param().expected_client_identity());
     }
     if (request->has_param() &&
         request->param().response_message_length() > 0) {
@@ -259,9 +319,18 @@
 class End2endTest : public ::testing::TestWithParam<bool> {
  protected:
   End2endTest()
-      : kMaxMessageSize_(8192), special_service_("special") {}
+      : is_server_started_(false),
+        kMaxMessageSize_(8192),
+        special_service_("special") {}
 
-  void SetUp() GRPC_OVERRIDE {
+  void TearDown() GRPC_OVERRIDE {
+    if (is_server_started_) {
+      server_->Shutdown();
+      if (proxy_server_) proxy_server_->Shutdown();
+    }
+  }
+
+  void StartServer(const std::shared_ptr<AuthMetadataProcessor>& processor) {
     int port = grpc_pick_unused_port_or_die();
     server_address_ << "127.0.0.1:" << port;
     // Setup server
@@ -271,22 +340,23 @@
     SslServerCredentialsOptions ssl_opts;
     ssl_opts.pem_root_certs = "";
     ssl_opts.pem_key_cert_pairs.push_back(pkcp);
-    builder.AddListeningPort(server_address_.str(),
-                             SslServerCredentials(ssl_opts));
+    auto server_creds = SslServerCredentials(ssl_opts);
+    server_creds->SetAuthMetadataProcessor(processor);
+    builder.AddListeningPort(server_address_.str(), server_creds);
     builder.RegisterService(&service_);
     builder.RegisterService("foo.test.youtube.com", &special_service_);
     builder.SetMaxMessageSize(
         kMaxMessageSize_);  // For testing max message size.
     builder.RegisterService(&dup_pkg_service_);
     server_ = builder.BuildAndStart();
-  }
-
-  void TearDown() GRPC_OVERRIDE {
-    server_->Shutdown();
-    if (proxy_server_) proxy_server_->Shutdown();
+    is_server_started_ = true;
   }
 
   void ResetChannel() {
+    if (!is_server_started_) {
+      StartServer(std::shared_ptr<AuthMetadataProcessor>());
+    }
+    EXPECT_TRUE(is_server_started_);
     SslCredentialsOptions ssl_opts = {test_root_cert, "", ""};
     ChannelArguments args;
     args.SetSslTargetNameOverride("foo.test.google.fr");
@@ -313,6 +383,7 @@
     stub_ = std::move(grpc::cpp::test::util::TestService::NewStub(channel_));
   }
 
+  bool is_server_started_;
   std::shared_ptr<Channel> channel_;
   std::unique_ptr<grpc::cpp::test::util::TestService::Stub> stub_;
   std::unique_ptr<Server> server_;
@@ -805,6 +876,82 @@
   EXPECT_TRUE(s.ok());
 }
 
+TEST_F(End2endTest, NonBlockingAuthMetadataProcessorSuccess) {
+  auto* processor = new TestAuthMetadataProcessor(false);
+  StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
+  ResetStub(false);
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  context.set_credentials(processor->GetCompatibleClientCreds());
+  request.set_message("Hello");
+  request.mutable_param()->set_echo_metadata(true);
+  request.mutable_param()->set_expected_client_identity(
+      TestAuthMetadataProcessor::kGoodGuy);
+
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(request.message(), response.message());
+  EXPECT_TRUE(s.ok());
+
+  // Metadata should have been consumed by the processor.
+  EXPECT_FALSE(MetadataContains(
+      context.GetServerTrailingMetadata(), GRPC_AUTHORIZATION_METADATA_KEY,
+      grpc::string("Bearer ") + TestAuthMetadataProcessor::kGoodGuy));
+}
+
+TEST_F(End2endTest, NonBlockingAuthMetadataProcessorFailure) {
+  auto* processor = new TestAuthMetadataProcessor(false);
+  StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
+  ResetStub(false);
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  context.set_credentials(processor->GetIncompatibleClientCreds());
+  request.set_message("Hello");
+
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
+}
+
+TEST_F(End2endTest, BlockingAuthMetadataProcessorSuccess) {
+  auto* processor = new TestAuthMetadataProcessor(true);
+  StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
+  ResetStub(false);
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  context.set_credentials(processor->GetCompatibleClientCreds());
+  request.set_message("Hello");
+  request.mutable_param()->set_echo_metadata(true);
+  request.mutable_param()->set_expected_client_identity(
+      TestAuthMetadataProcessor::kGoodGuy);
+
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_EQ(request.message(), response.message());
+  EXPECT_TRUE(s.ok());
+
+  // Metadata should have been consumed by the processor.
+  EXPECT_FALSE(MetadataContains(
+      context.GetServerTrailingMetadata(), GRPC_AUTHORIZATION_METADATA_KEY,
+      grpc::string("Bearer ") + TestAuthMetadataProcessor::kGoodGuy));
+}
+
+TEST_F(End2endTest, BlockingAuthMetadataProcessorFailure) {
+  auto* processor = new TestAuthMetadataProcessor(true);
+  StartServer(std::shared_ptr<AuthMetadataProcessor>(processor));
+  ResetStub(false);
+  EchoRequest request;
+  EchoResponse response;
+  ClientContext context;
+  context.set_credentials(processor->GetIncompatibleClientCreds());
+  request.set_message("Hello");
+
+  Status s = stub_->Echo(&context, request, &response);
+  EXPECT_FALSE(s.ok());
+  EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
+}
+
 // Client sends 20 requests and the server returns CANCELLED status after
 // reading 10 requests.
 TEST_F(End2endTest, RequestStreamServerEarlyCancelTest) {
diff --git a/test/cpp/util/messages.proto b/test/cpp/util/messages.proto
index 359d1db..a022707 100644
--- a/test/cpp/util/messages.proto
+++ b/test/cpp/util/messages.proto
@@ -40,6 +40,7 @@
   bool check_auth_context = 5;
   int32 response_message_length = 6;
   bool echo_peer = 7;
+  string expected_client_identity = 8;  // will force check_auth_context.
 }
 
 message EchoRequest {
diff --git a/tools/doxygen/Doxyfile.c++ b/tools/doxygen/Doxyfile.c++
index 887f616..0cd6ddd 100644
--- a/tools/doxygen/Doxyfile.c++
+++ b/tools/doxygen/Doxyfile.c++
@@ -760,7 +760,8 @@
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = include/grpc++/channel.h \
+INPUT                  = include/grpc++/auth_metadata_processor.h \
+include/grpc++/channel.h \
 include/grpc++/client_context.h \
 include/grpc++/completion_queue.h \
 include/grpc++/create_channel.h \
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index 84b13f9..b2ebb2d 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -760,7 +760,8 @@
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = include/grpc++/channel.h \
+INPUT                  = include/grpc++/auth_metadata_processor.h \
+include/grpc++/channel.h \
 include/grpc++/client_context.h \
 include/grpc++/completion_queue.h \
 include/grpc++/create_channel.h \
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 0585b82..4840e24 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -13084,6 +13084,7 @@
       "grpc"
     ], 
     "headers": [
+      "include/grpc++/auth_metadata_processor.h", 
       "include/grpc++/channel.h", 
       "include/grpc++/client_context.h", 
       "include/grpc++/completion_queue.h", 
@@ -13135,6 +13136,7 @@
     "language": "c++", 
     "name": "grpc++", 
     "src": [
+      "include/grpc++/auth_metadata_processor.h", 
       "include/grpc++/channel.h", 
       "include/grpc++/client_context.h", 
       "include/grpc++/completion_queue.h", 
@@ -13264,6 +13266,7 @@
       "grpc_unsecure"
     ], 
     "headers": [
+      "include/grpc++/auth_metadata_processor.h", 
       "include/grpc++/channel.h", 
       "include/grpc++/client_context.h", 
       "include/grpc++/completion_queue.h", 
@@ -13312,6 +13315,7 @@
     "language": "c++", 
     "name": "grpc++_unsecure", 
     "src": [
+      "include/grpc++/auth_metadata_processor.h", 
       "include/grpc++/channel.h", 
       "include/grpc++/client_context.h", 
       "include/grpc++/completion_queue.h", 
diff --git a/vsprojects/grpc++/grpc++.vcxproj b/vsprojects/grpc++/grpc++.vcxproj
index 53930c1..0d8cf1d 100644
--- a/vsprojects/grpc++/grpc++.vcxproj
+++ b/vsprojects/grpc++/grpc++.vcxproj
@@ -213,6 +213,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClInclude Include="..\..\include\grpc++\auth_metadata_processor.h" />
     <ClInclude Include="..\..\include\grpc++\channel.h" />
     <ClInclude Include="..\..\include\grpc++\client_context.h" />
     <ClInclude Include="..\..\include\grpc++\completion_queue.h" />
diff --git a/vsprojects/grpc++/grpc++.vcxproj.filters b/vsprojects/grpc++/grpc++.vcxproj.filters
index 6bc9ed6..e38328b 100644
--- a/vsprojects/grpc++/grpc++.vcxproj.filters
+++ b/vsprojects/grpc++/grpc++.vcxproj.filters
@@ -99,6 +99,9 @@
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="..\..\include\grpc++\auth_metadata_processor.h">
+      <Filter>include\grpc++</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\include\grpc++\channel.h">
       <Filter>include\grpc++</Filter>
     </ClInclude>
diff --git a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj
index 12fa1b7..d5e44d6 100644
--- a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj
+++ b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj
@@ -213,6 +213,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClInclude Include="..\..\include\grpc++\auth_metadata_processor.h" />
     <ClInclude Include="..\..\include\grpc++\channel.h" />
     <ClInclude Include="..\..\include\grpc++\client_context.h" />
     <ClInclude Include="..\..\include\grpc++\completion_queue.h" />
diff --git a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
index 17d9d53..da7d87e 100644
--- a/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
+++ b/vsprojects/grpc++_unsecure/grpc++_unsecure.vcxproj.filters
@@ -84,6 +84,9 @@
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="..\..\include\grpc++\auth_metadata_processor.h">
+      <Filter>include\grpc++</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\include\grpc++\channel.h">
       <Filter>include\grpc++</Filter>
     </ClInclude>