diff --git a/src/csharp/Grpc.Auth/AuthInterceptors.cs b/src/csharp/Grpc.Auth/AuthInterceptors.cs
index c8ab4d9af6f8966b6dcc1354ee1da6c02954adfc..fa9256677518d13c205a191827ab4d72ca171ee5 100644
--- a/src/csharp/Grpc.Auth/AuthInterceptors.cs
+++ b/src/csharp/Grpc.Auth/AuthInterceptors.cs
@@ -41,8 +41,8 @@ using Grpc.Core.Utils;
 namespace Grpc.Auth
 {
     /// <summary>
-    /// Factory methods to create authorization interceptors. Interceptors created can be registered with gRPC client classes (autogenerated client stubs that
-    /// inherit from <see cref="Grpc.Core.ClientBase"/>).
+    /// Factory methods to create authorization interceptors.
+    /// <seealso cref="GrpcCredentials"/>
     /// </summary>
     public static class AuthInterceptors
     {
@@ -50,31 +50,29 @@ namespace Grpc.Auth
         private const string Schema = "Bearer";
 
         /// <summary>
-        /// Creates interceptor that will obtain access token from any credential type that implements
+        /// Creates an <see cref="AsyncAuthInterceptor"/> that will obtain access token from any credential type that implements
         /// <c>ITokenAccess</c>. (e.g. <c>GoogleCredential</c>).
         /// </summary>
         /// <param name="credential">The credential to use to obtain access tokens.</param>
-        /// <returns>The header interceptor.</returns>
-        public static HeaderInterceptor FromCredential(ITokenAccess credential)
+        /// <returns>The interceptor.</returns>
+        public static AsyncAuthInterceptor FromCredential(ITokenAccess credential)
         {
-            return new HeaderInterceptor((method, authUri, metadata) =>
+            return new AsyncAuthInterceptor(async (authUri, metadata) =>
             {
-                // TODO(jtattermusch): Rethink synchronous wait to obtain the result.
-                var accessToken = credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None)
-                        .ConfigureAwait(false).GetAwaiter().GetResult();
+                var accessToken = await credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None).ConfigureAwait(false);
                 metadata.Add(CreateBearerTokenHeader(accessToken));
             });
         }
 
         /// <summary>
-        /// Creates OAuth2 interceptor that will use given access token as authorization.
+        /// Creates an <see cref="AsyncAuthInterceptor"/> that will use given access token as authorization.
         /// </summary>
         /// <param name="accessToken">OAuth2 access token.</param>
-        /// <returns>The header interceptor.</returns>
-        public static HeaderInterceptor FromAccessToken(string accessToken)
+        /// <returns>The interceptor.</returns>
+        public static AsyncAuthInterceptor FromAccessToken(string accessToken)
         {
             Preconditions.CheckNotNull(accessToken);
-            return new HeaderInterceptor((method, authUri, metadata) =>
+            return new AsyncAuthInterceptor(async (authUri, metadata) =>
             {
                 metadata.Add(CreateBearerTokenHeader(accessToken));
             });
diff --git a/src/csharp/Grpc.Auth/Grpc.Auth.csproj b/src/csharp/Grpc.Auth/Grpc.Auth.csproj
index 4fb087d4a342686c0acc4d75da04ea22240e851a..80ab07d2ae531222ef1b5f93823818c6db2bb892 100644
--- a/src/csharp/Grpc.Auth/Grpc.Auth.csproj
+++ b/src/csharp/Grpc.Auth/Grpc.Auth.csproj
@@ -78,6 +78,7 @@
     <Compile Include="..\Grpc.Core\Version.cs">
       <Link>Version.cs</Link>
     </Compile>
+    <Compile Include="GrpcCredentials.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="AuthInterceptors.cs" />
   </ItemGroup>
diff --git a/src/csharp/Grpc.Auth/GrpcCredentials.cs b/src/csharp/Grpc.Auth/GrpcCredentials.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d8b10804c6e2959c7f77287a5408bcce064e3f0e
--- /dev/null
+++ b/src/csharp/Grpc.Auth/GrpcCredentials.cs
@@ -0,0 +1,93 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using System;
+using System.Threading;
+
+using Google.Apis.Auth.OAuth2;
+using Grpc.Core;
+using Grpc.Core.Utils;
+
+namespace Grpc.Auth
+{
+    /// <summary>
+    /// Factory methods to create instances of <see cref="ChannelCredentials"/> and <see cref="CallCredentials"/> classes.
+    /// </summary>
+    public static class GrpcCredentials
+    {
+        /// <summary>
+        /// Creates a <see cref="MetadataCredentials"/> instance that will obtain access tokens 
+        /// from any credential that implements <c>ITokenAccess</c>. (e.g. <c>GoogleCredential</c>).
+        /// </summary>
+        /// <param name="credential">The credential to use to obtain access tokens.</param>
+        /// <returns>The <c>MetadataCredentials</c> instance.</returns>
+        public static MetadataCredentials Create(ITokenAccess credential)
+        {
+            return new MetadataCredentials(AuthInterceptors.FromCredential(credential));
+        }
+
+        /// <summary>
+        /// Convenience method to create a <see cref="ChannelCredentials"/> instance from
+        /// <c>ITokenAccess</c> credential and <c>SslCredentials</c> instance.
+        /// </summary>
+        /// <param name="credential">The credential to use to obtain access tokens.</param>
+        /// <param name="sslCredentials">The <c>SslCredentials</c> instance.</param>
+        /// <returns>The channel credentials for access token based auth over a secure channel.</returns>
+        public static ChannelCredentials Create(ITokenAccess credential, SslCredentials sslCredentials)
+        {
+            return ChannelCredentials.Create(sslCredentials, Create(credential));
+        }
+
+        /// <summary>
+        /// Creates an instance of <see cref="MetadataCredentials"/> that will use given access token to authenticate
+        /// with a gRPC service.
+        /// </summary>
+        /// <param name="accessToken">OAuth2 access token.</param>
+        /// /// <returns>The <c>MetadataCredentials</c> instance.</returns>
+        public static MetadataCredentials FromAccessToken(string accessToken)
+        {
+            return new MetadataCredentials(AuthInterceptors.FromAccessToken(accessToken));
+        }
+
+        /// <summary>
+        /// Converts a <c>ITokenAccess</c> object into a <see cref="MetadataCredentials"/> object supported
+        /// by gRPC.
+        /// </summary>
+        /// <param name="credential"></param>
+        /// <returns></returns>
+        public static MetadataCredentials ToGrpcCredentials(this ITokenAccess credential)
+        {
+            return GrpcCredentials.Create(credential);
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/ClientBaseTest.cs b/src/csharp/Grpc.Core.Tests/CallCredentialsTest.cs
similarity index 66%
rename from src/csharp/Grpc.Core.Tests/ClientBaseTest.cs
rename to src/csharp/Grpc.Core.Tests/CallCredentialsTest.cs
index 2dc10ebe971a2c536bda5dc903b93e23f2b9c389..451963229a40c32f711727994bad9fc978c84cd9 100644
--- a/src/csharp/Grpc.Core.Tests/ClientBaseTest.cs
+++ b/src/csharp/Grpc.Core.Tests/CallCredentialsTest.cs
@@ -32,6 +32,10 @@
 #endregion
 
 using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
 using Grpc.Core;
 using Grpc.Core.Internal;
 using Grpc.Core.Utils;
@@ -39,24 +43,23 @@ using NUnit.Framework;
 
 namespace Grpc.Core.Tests
 {
-    public class ClientBaseTest
+    public class CallCredentialsTest
     {
         [Test]
-        public void GetAuthUriBase_Valid()
+        public void CallCredentials_ComposeAtLeastTwo()
         {
-            Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("some.googleapi.com"));
-            Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("dns:///some.googleapi.com/"));
-            Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("dns:///some.googleapi.com:443/"));
-            Assert.AreEqual("https://some.googleapi.com/", ClientBase.GetAuthUriBase("some.googleapi.com:443/"));
+            Assert.Throws(typeof(ArgumentException), () => CallCredentials.Compose(new FakeCallCredentials()));
         }
 
         [Test]
-        public void GetAuthUriBase_Invalid()
+        public void CallCredentials_ToNativeCredentials()
         {
-            Assert.IsNull(ClientBase.GetAuthUriBase("some.googleapi.com:"));
-            Assert.IsNull(ClientBase.GetAuthUriBase("https://some.googleapi.com/"));
-            Assert.IsNull(ClientBase.GetAuthUriBase("dns://some.googleapi.com:443"));  // just two slashes
-            Assert.IsNull(ClientBase.GetAuthUriBase(""));
+            var composite = CallCredentials.Compose(
+                new MetadataCredentials(async (uri, m) => { await Task.Delay(1); }),
+                new MetadataCredentials(async (uri, m) => { await Task.Delay(2); }));
+            using (var nativeComposite = composite.ToNativeCredentials())
+            {
+            }
         }
     }
 }
diff --git a/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs b/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..489bf38575686a3367d840abf2e00b4ed2623617
--- /dev/null
+++ b/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
@@ -0,0 +1,73 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+namespace Grpc.Core.Tests
+{
+    public class ChannelCredentialsTest
+    {
+        [Test]
+        public void InsecureCredentials_IsNonComposable()
+        {
+            Assert.IsFalse(ChannelCredentials.Insecure.IsComposable);
+        }
+
+        [Test]
+        public void ChannelCredentials_CreateComposite()
+        {
+            var composite = ChannelCredentials.Create(new FakeChannelCredentials(true), new FakeCallCredentials());
+            Assert.IsFalse(composite.IsComposable);
+
+            Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(null, new FakeCallCredentials()));
+            Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(new FakeChannelCredentials(true), null));
+            
+            // forbid composing non-composable
+            Assert.Throws(typeof(ArgumentException), () => ChannelCredentials.Create(new FakeChannelCredentials(false), new FakeCallCredentials()));
+        }
+
+        [Test]
+        public void ChannelCredentials_CreateWrapped()
+        {
+            ChannelCredentials.Create(new FakeCallCredentials());
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/ChannelTest.cs b/src/csharp/Grpc.Core.Tests/ChannelTest.cs
index dfbd92879e7cb969b7f7f9225b5de3bc34d1a8cf..f4ae9abefd803002a91685a86400260b471824bc 100644
--- a/src/csharp/Grpc.Core.Tests/ChannelTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ChannelTest.cs
@@ -44,13 +44,13 @@ namespace Grpc.Core.Tests
         [Test]
         public void Constructor_RejectsInvalidParams()
         {
-            Assert.Throws(typeof(ArgumentNullException), () => new Channel(null, Credentials.Insecure));
+            Assert.Throws(typeof(ArgumentNullException), () => new Channel(null, ChannelCredentials.Insecure));
         }
 
         [Test]
         public void State_IdleAfterCreation()
         {
-            var channel = new Channel("localhost", Credentials.Insecure);
+            var channel = new Channel("localhost", ChannelCredentials.Insecure);
             Assert.AreEqual(ChannelState.Idle, channel.State);
             channel.ShutdownAsync().Wait();
         }
@@ -58,7 +58,7 @@ namespace Grpc.Core.Tests
         [Test]
         public void WaitForStateChangedAsync_InvalidArgument()
         {
-            var channel = new Channel("localhost", Credentials.Insecure);
+            var channel = new Channel("localhost", ChannelCredentials.Insecure);
             Assert.Throws(typeof(ArgumentException), () => channel.WaitForStateChangedAsync(ChannelState.FatalFailure));
             channel.ShutdownAsync().Wait();
         }
@@ -66,7 +66,7 @@ namespace Grpc.Core.Tests
         [Test]
         public void ResolvedTarget()
         {
-            var channel = new Channel("127.0.0.1", Credentials.Insecure);
+            var channel = new Channel("127.0.0.1", ChannelCredentials.Insecure);
             Assert.IsTrue(channel.ResolvedTarget.Contains("127.0.0.1"));
             channel.ShutdownAsync().Wait();
         }
@@ -74,7 +74,7 @@ namespace Grpc.Core.Tests
         [Test]
         public void Shutdown_AllowedOnlyOnce()
         {
-            var channel = new Channel("localhost", Credentials.Insecure);
+            var channel = new Channel("localhost", ChannelCredentials.Insecure);
             channel.ShutdownAsync().Wait();
             Assert.Throws(typeof(InvalidOperationException), () => channel.ShutdownAsync().GetAwaiter().GetResult());
         }
diff --git a/src/csharp/Grpc.Core.Tests/FakeCredentials.cs b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs
new file mode 100644
index 0000000000000000000000000000000000000000..87d55cd276ae5601b84a1dd3b7db97dd4aac5fe7
--- /dev/null
+++ b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs
@@ -0,0 +1,73 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+namespace Grpc.Core.Tests
+{
+    internal class FakeChannelCredentials : ChannelCredentials
+    {
+        readonly bool composable;
+
+        public FakeChannelCredentials(bool composable)
+        {
+            this.composable = composable;
+        }
+
+        internal override bool IsComposable
+        {
+            get { return composable; }
+        }
+
+        internal override CredentialsSafeHandle ToNativeCredentials()
+        {
+            return null;
+        }
+    }
+
+    internal class FakeCallCredentials : CallCredentials
+    {
+        internal override CredentialsSafeHandle ToNativeCredentials()
+        {
+            return null;
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
index f730936062daa1efeec6c00c425a27cbcde9a25d..91d072ababecdd6360923320f302fd7a086b74a2 100644
--- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
+++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
@@ -63,8 +63,10 @@
     <Compile Include="..\Grpc.Core\Version.cs">
       <Link>Version.cs</Link>
     </Compile>
-    <Compile Include="ClientBaseTest.cs" />
+    <Compile Include="CallCredentialsTest.cs" />
+    <Compile Include="FakeCredentials.cs" />
     <Compile Include="MarshallingErrorsTest.cs" />
+    <Compile Include="ChannelCredentialsTest.cs" />
     <Compile Include="ShutdownTest.cs" />
     <Compile Include="Internal\AsyncCallTest.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
index 685c5f7d6cb7a314cbfdee015e07ed79cd1a5efd..246072bff1305cc605e671fa3c766eb5d235acb7 100644
--- a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
+++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
@@ -49,7 +49,7 @@ namespace Grpc.Core.Internal.Tests
         [SetUp]
         public void Init()
         {
-            channel = new Channel("localhost", Credentials.Insecure);
+            channel = new Channel("localhost", ChannelCredentials.Insecure);
 
             fakeCall = new FakeNativeCall();
 
diff --git a/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs b/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs
index 83707e0c6da91c35da05d321341a6617537925d0..37fb36946aff1a3d8a5fce783eb94ea72e50e9bc 100644
--- a/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs
+++ b/src/csharp/Grpc.Core.Tests/MarshallingErrorsTest.cs
@@ -119,7 +119,7 @@ namespace Grpc.Core.Tests
         [Test]
         public void RequestParsingError_UnaryRequest()
         {
-            helper.UnaryHandler = new  UnaryServerMethod<string, string>((request, context) =>
+            helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
             {
                 return Task.FromResult("RESPONSE");
             });
@@ -161,7 +161,7 @@ namespace Grpc.Core.Tests
         {
             helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
             {
-                CollectionAssert.AreEqual(new [] {"A", "B"}, await requestStream.ToListAsync());
+                CollectionAssert.AreEqual(new[] { "A", "B" }, await requestStream.ToListAsync());
                 return "RESPONSE";
             });
             var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall());
diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
index 765732c7687eaa7a34e490cc6ad93c450fad5f9c..567e04eddccbc4cf95d1a11b79267ca1571fae23 100644
--- a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
+++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
@@ -154,7 +154,7 @@ namespace Grpc.Core.Tests
         {
             if (channel == null)
             {
-                channel = new Channel(Host, GetServer().Ports.Single().BoundPort, Credentials.Insecure);
+                channel = new Channel(Host, GetServer().Ports.Single().BoundPort, ChannelCredentials.Insecure);
             }
             return channel;
         }
diff --git a/src/csharp/Grpc.Core/CallCredentials.cs b/src/csharp/Grpc.Core/CallCredentials.cs
new file mode 100644
index 0000000000000000000000000000000000000000..809c9f412d0ed4bf4126561fb5a6b9f5b29fe89f
--- /dev/null
+++ b/src/csharp/Grpc.Core/CallCredentials.cs
@@ -0,0 +1,142 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core
+{
+    /// <summary>
+    /// Client-side call credentials. Provide authorization with per-call granularity.
+    /// </summary>
+    public abstract class CallCredentials
+    {
+        /// <summary>
+        /// Composes multiple multiple <c>CallCredentials</c> objects into
+        /// a single <c>CallCredentials</c> object.
+        /// </summary>
+        /// <param name="credentials">credentials to compose</param>
+        /// <returns>The new <c>CompositeCallCredentials</c></returns>
+        public static CallCredentials Compose(params CallCredentials[] credentials)
+        {
+            return new CompositeCallCredentials(credentials);
+        }
+
+        /// <summary>
+        /// Creates native object for the credentials.
+        /// </summary>
+        /// <returns>The native credentials.</returns>
+        internal abstract CredentialsSafeHandle ToNativeCredentials();
+    }
+
+    /// <summary>
+    /// Asynchronous authentication interceptor for <see cref="MetadataCredentials"/>.
+    /// </summary>
+    /// <param name="authUri">URL of a service to which current remote call needs to authenticate</param>
+    /// <param name="metadata">Metadata to populate with entries that will be added to outgoing call's headers.</param>
+    /// <returns></returns>
+    public delegate Task AsyncAuthInterceptor(string authUri, Metadata metadata);
+
+    /// <summary>
+    /// Client-side credentials that delegate metadata based auth to an interceptor.
+    /// The interceptor is automatically invoked for each remote call that uses <c>MetadataCredentials.</c>
+    /// </summary>
+    public class MetadataCredentials : CallCredentials
+    {
+        readonly AsyncAuthInterceptor interceptor;
+
+        /// <summary>
+        /// Initializes a new instance of <c>MetadataCredentials</c> class.
+        /// </summary>
+        /// <param name="interceptor">authentication interceptor</param>
+        public MetadataCredentials(AsyncAuthInterceptor interceptor)
+        {
+            this.interceptor = interceptor;
+        }
+
+        internal override CredentialsSafeHandle ToNativeCredentials()
+        {
+            NativeMetadataCredentialsPlugin plugin = new NativeMetadataCredentialsPlugin(interceptor);
+            return plugin.Credentials;
+        }
+    }
+
+    /// <summary>
+    /// Credentials that allow composing multiple credentials objects into one <see cref="CallCredentials"/> object.
+    /// </summary>
+    internal sealed class CompositeCallCredentials : CallCredentials
+    {
+        readonly List<CallCredentials> credentials;
+
+        /// <summary>
+        /// Initializes a new instance of <c>CompositeCallCredentials</c> class.
+        /// The resulting credentials object will be composite of all the credentials specified as parameters.
+        /// </summary>
+        /// <param name="credentials">credentials to compose</param>
+        public CompositeCallCredentials(params CallCredentials[] credentials)
+        {
+            Preconditions.CheckArgument(credentials.Length >= 2, "Composite credentials object can only be created from 2 or more credentials.");
+            this.credentials = new List<CallCredentials>(credentials);
+        }
+
+        internal override CredentialsSafeHandle ToNativeCredentials()
+        {
+            return ToNativeRecursive(0);
+        }
+
+        // Recursive descent makes managing lifetime of intermediate CredentialSafeHandle instances easier.
+        // In practice, we won't usually see composites from more than two credentials anyway.
+        private CredentialsSafeHandle ToNativeRecursive(int startIndex)
+        {
+            if (startIndex == credentials.Count - 1)
+            {
+                return credentials[startIndex].ToNativeCredentials();
+            }
+
+            using (var cred1 = credentials[startIndex].ToNativeCredentials())
+            using (var cred2 = ToNativeRecursive(startIndex + 1))
+            {
+                var nativeComposite = CredentialsSafeHandle.CreateComposite(cred1, cred2);
+                if (nativeComposite.IsInvalid)
+                {
+                    throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
+                }
+                return nativeComposite;
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs
index c3bc9c31564753582c156b54a169353c8662430e..c0f94c63c2f01bc3853fd7b42f55b2f6812a1d94 100644
--- a/src/csharp/Grpc.Core/CallOptions.cs
+++ b/src/csharp/Grpc.Core/CallOptions.cs
@@ -49,6 +49,7 @@ namespace Grpc.Core
         CancellationToken cancellationToken;
         WriteOptions writeOptions;
         ContextPropagationToken propagationToken;
+        CallCredentials credentials;
 
         /// <summary>
         /// Creates a new instance of <c>CallOptions</c> struct.
@@ -58,14 +59,16 @@ namespace Grpc.Core
         /// <param name="cancellationToken">Can be used to request cancellation of the call.</param>
         /// <param name="writeOptions">Write options that will be used for this call.</param>
         /// <param name="propagationToken">Context propagation token obtained from <see cref="ServerCallContext"/>.</param>
+        /// <param name="credentials">Credentials to use for this call.</param>
         public CallOptions(Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken),
-                           WriteOptions writeOptions = null, ContextPropagationToken propagationToken = null)
+                           WriteOptions writeOptions = null, ContextPropagationToken propagationToken = null, CallCredentials credentials = null)
         {
             this.headers = headers;
             this.deadline = deadline;
             this.cancellationToken = cancellationToken;
             this.writeOptions = writeOptions;
             this.propagationToken = propagationToken;
+            this.credentials = credentials;
         }
 
         /// <summary>
@@ -114,6 +117,17 @@ namespace Grpc.Core
             }
         }
 
+        /// <summary>
+        /// Credentials to use for this call.
+        /// </summary>
+        public CallCredentials Credentials
+        {
+            get
+            {
+                return this.credentials;
+            }
+        }
+
         /// <summary>
         /// Returns new instance of <see cref="CallOptions"/> with
         /// <c>Headers</c> set to the value provided. Values of all other fields are preserved.
diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs
index f1942727cde48a9837a533332e7f9a34bcd3aac1..6b99055d4c893cb7b92d703d11b4bdbc2360fb71 100644
--- a/src/csharp/Grpc.Core/Channel.cs
+++ b/src/csharp/Grpc.Core/Channel.cs
@@ -68,7 +68,7 @@ namespace Grpc.Core
         /// <param name="target">Target of the channel.</param>
         /// <param name="credentials">Credentials to secure the channel.</param>
         /// <param name="options">Channel options.</param>
-        public Channel(string target, Credentials credentials, IEnumerable<ChannelOption> options = null)
+        public Channel(string target, ChannelCredentials credentials, IEnumerable<ChannelOption> options = null)
         {
             this.target = Preconditions.CheckNotNull(target, "target");
             this.environment = GrpcEnvironment.AddRef();
@@ -96,7 +96,7 @@ namespace Grpc.Core
         /// <param name="port">The port.</param>
         /// <param name="credentials">Credentials to secure the channel.</param>
         /// <param name="options">Channel options.</param>
-        public Channel(string host, int port, Credentials credentials, IEnumerable<ChannelOption> options = null) :
+        public Channel(string host, int port, ChannelCredentials credentials, IEnumerable<ChannelOption> options = null) :
             this(string.Format("{0}:{1}", host, port), credentials, options)
         {
         }
diff --git a/src/csharp/Grpc.Core/ChannelCredentials.cs b/src/csharp/Grpc.Core/ChannelCredentials.cs
new file mode 100644
index 0000000000000000000000000000000000000000..599674e02bdbf71d430097693888c04da7318601
--- /dev/null
+++ b/src/csharp/Grpc.Core/ChannelCredentials.cs
@@ -0,0 +1,238 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core
+{
+    /// <summary>
+    /// Client-side channel credentials. Used for creation of a secure channel.
+    /// </summary>
+    public abstract class ChannelCredentials
+    {
+        static readonly ChannelCredentials InsecureInstance = new InsecureCredentialsImpl();
+
+        /// <summary>
+        /// Returns instance of credentials that provides no security and 
+        /// will result in creating an unsecure channel with no encryption whatsoever.
+        /// </summary>
+        public static ChannelCredentials Insecure
+        {
+            get
+            {
+                return InsecureInstance;
+            }
+        }
+
+        /// <summary>
+        /// Creates a new instance of <c>ChannelCredentials</c> class by composing
+        /// given channel credentials with call credentials.
+        /// </summary>
+        /// <param name="channelCredentials">Channel credentials.</param>
+        /// <param name="callCredentials">Call credentials.</param>
+        /// <returns>The new composite <c>ChannelCredentials</c></returns>
+        public static ChannelCredentials Create(ChannelCredentials channelCredentials, CallCredentials callCredentials)
+        {
+            return new CompositeChannelCredentials(channelCredentials, callCredentials);
+        }
+
+        /// <summary>
+        /// Creates a new instance of <c>ChannelCredentials</c> by wrapping
+        /// an instance of <c>CallCredentials</c>.
+        /// </summary>
+        /// <param name="callCredentials">Call credentials.</param>
+        /// <returns>The <c>ChannelCredentials</c> wrapping given call credentials.</returns>
+        public static ChannelCredentials Create(CallCredentials callCredentials)
+        {
+            return new WrappedCallCredentials(callCredentials);
+        }
+
+        /// <summary>
+        /// Creates native object for the credentials. May return null if insecure channel
+        /// should be created.
+        /// </summary>
+        /// <returns>The native credentials.</returns>
+        internal abstract CredentialsSafeHandle ToNativeCredentials();
+
+        /// <summary>
+        /// Returns <c>true</c> if this credential type allows being composed by <c>CompositeCredentials</c>.
+        /// </summary>
+        internal virtual bool IsComposable
+        {
+            get { return false; }
+        }
+
+        private sealed class InsecureCredentialsImpl : ChannelCredentials
+        {
+            internal override CredentialsSafeHandle ToNativeCredentials()
+            {
+                return null;
+            }
+        }
+    }
+
+    /// <summary>
+    /// Client-side SSL credentials.
+    /// </summary>
+    public sealed class SslCredentials : ChannelCredentials
+    {
+        readonly string rootCertificates;
+        readonly KeyCertificatePair keyCertificatePair;
+
+        /// <summary>
+        /// Creates client-side SSL credentials loaded from
+        /// disk file pointed to by the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable.
+        /// If that fails, gets the roots certificates from a well known place on disk.
+        /// </summary>
+        public SslCredentials() : this(null, null)
+        {
+        }
+
+        /// <summary>
+        /// Creates client-side SSL credentials from
+        /// a string containing PEM encoded root certificates.
+        /// </summary>
+        public SslCredentials(string rootCertificates) : this(rootCertificates, null)
+        {
+        }
+            
+        /// <summary>
+        /// Creates client-side SSL credentials.
+        /// </summary>
+        /// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>
+        /// <param name="keyCertificatePair">a key certificate pair.</param>
+        public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair)
+        {
+            this.rootCertificates = rootCertificates;
+            this.keyCertificatePair = keyCertificatePair;
+        }
+
+        /// <summary>
+        /// PEM encoding of the server root certificates.
+        /// </summary>
+        public string RootCertificates
+        {
+            get
+            {
+                return this.rootCertificates;
+            }
+        }
+
+        /// <summary>
+        /// Client side key and certificate pair.
+        /// If null, client will not use key and certificate pair.
+        /// </summary>
+        public KeyCertificatePair KeyCertificatePair
+        {
+            get
+            {
+                return this.keyCertificatePair;
+            }
+        }
+
+        // Composing composite makes no sense.
+        internal override bool IsComposable
+        {
+            get { return true; }
+        }
+
+        internal override CredentialsSafeHandle ToNativeCredentials()
+        {
+            return CredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair);
+        }
+    }
+
+    /// <summary>
+    /// Credentials that allow composing one <see cref="ChannelCredentials"/> object and 
+    /// one or more <see cref="CallCredentials"/> objects into a single <see cref="ChannelCredentials"/>.
+    /// </summary>
+    internal sealed class CompositeChannelCredentials : ChannelCredentials
+    {
+        readonly ChannelCredentials channelCredentials;
+        readonly CallCredentials callCredentials;
+
+        /// <summary>
+        /// Initializes a new instance of <c>CompositeChannelCredentials</c> class.
+        /// The resulting credentials object will be composite of all the credentials specified as parameters.
+        /// </summary>
+        /// <param name="channelCredentials">channelCredentials to compose</param>
+        /// <param name="callCredentials">channelCredentials to compose</param>
+        public CompositeChannelCredentials(ChannelCredentials channelCredentials, CallCredentials callCredentials)
+        {
+            this.channelCredentials = Preconditions.CheckNotNull(channelCredentials);
+            this.callCredentials = Preconditions.CheckNotNull(callCredentials);
+            Preconditions.CheckArgument(channelCredentials.IsComposable, "Supplied channel credentials do not allow composition.");
+        }
+
+        internal override CredentialsSafeHandle ToNativeCredentials()
+        {
+            using (var cred1 = channelCredentials.ToNativeCredentials())
+            using (var cred2 = callCredentials.ToNativeCredentials())
+            {
+                var nativeComposite = CredentialsSafeHandle.CreateComposite(cred1, cred2);
+                if (nativeComposite.IsInvalid)
+                {
+                    throw new ArgumentException("Error creating native composite credentials. Likely, this is because you are trying to compose incompatible credentials.");
+                }
+                return nativeComposite;
+            }
+        }
+    }
+
+    /// <summary>
+    /// Credentials wrapping <see cref="CallCredentials"/> as <see cref="ChannelCredentials"/>.
+    /// </summary>
+    internal sealed class WrappedCallCredentials : ChannelCredentials
+    {
+        readonly CallCredentials callCredentials;
+
+        /// <summary>
+        /// Wraps instance of <c>CallCredentials</c> as <c>ChannelCredentials</c>.
+        /// </summary>
+        /// <param name="callCredentials">credentials to wrap</param>
+        public WrappedCallCredentials(CallCredentials callCredentials)
+        {
+            this.callCredentials = Preconditions.CheckNotNull(callCredentials);
+        }
+
+        internal override CredentialsSafeHandle ToNativeCredentials()
+        {
+            return callCredentials.ToNativeCredentials();
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/ClientBase.cs b/src/csharp/Grpc.Core/ClientBase.cs
index f4533e735cbc802fbab34ee1766c13b3a5af7c7f..e5b398062b2c489891d38cc71ffee03eb274e059 100644
--- a/src/csharp/Grpc.Core/ClientBase.cs
+++ b/src/csharp/Grpc.Core/ClientBase.cs
@@ -40,18 +40,17 @@ namespace Grpc.Core
     /// <summary>
     /// Interceptor for call headers.
     /// </summary>
-    public delegate void HeaderInterceptor(IMethod method, string authUri, Metadata metadata);
+    /// <remarks>Header interceptor is no longer to recommented way to perform authentication.
+    /// For header (initial metadata) based auth such as OAuth2 or JWT access token, use <see cref="MetadataCredentials"/>.
+    /// </remarks>
+    public delegate void HeaderInterceptor(IMethod method, Metadata metadata);
 
     /// <summary>
     /// Base class for client-side stubs.
     /// </summary>
     public abstract class ClientBase
     {
-        // Regex for removal of the optional DNS scheme, trailing port, and trailing backslash
-        static readonly Regex ChannelTargetPattern = new Regex(@"^(dns:\/{3})?([^:\/]+)(:\d+)?\/?$");
-
         readonly Channel channel;
-        readonly string authUriBase;
 
         /// <summary>
         /// Initializes a new instance of <c>ClientBase</c> class.
@@ -60,13 +59,14 @@ namespace Grpc.Core
         public ClientBase(Channel channel)
         {
             this.channel = channel;
-            this.authUriBase = GetAuthUriBase(channel.Target);
         }
 
         /// <summary>
-        /// Can be used to register a custom header (request metadata) interceptor.
+        /// Can be used to register a custom header interceptor.
         /// The interceptor is invoked each time a new call on this client is started.
+        /// It is not recommented to use header interceptor to add auth headers to RPC calls.
         /// </summary>
+        /// <seealso cref="HeaderInterceptor"/>
         public HeaderInterceptor HeaderInterceptor
         {
             get;
@@ -115,24 +115,9 @@ namespace Grpc.Core
                 {
                     options = options.WithHeaders(new Metadata());
                 }
-                var authUri = authUriBase != null ? authUriBase + method.ServiceName : null;
-                interceptor(method, authUri, options.Headers);
+                interceptor(method, options.Headers);
             }
             return new CallInvocationDetails<TRequest, TResponse>(channel, method, Host, options);
         }
-
-        /// <summary>
-        /// Creates Auth URI base from channel's target (the one passed at channel creation).
-        /// Fully-qualified service name is to be appended to this.
-        /// </summary>
-        internal static string GetAuthUriBase(string target)
-        {
-            var match = ChannelTargetPattern.Match(target);
-            if (!match.Success)
-            {
-                return null;
-            }
-            return "https://" + match.Groups[2].Value + "/";
-        }
     }
 }
diff --git a/src/csharp/Grpc.Core/Credentials.cs b/src/csharp/Grpc.Core/Credentials.cs
deleted file mode 100644
index 4fcac0c4c00e97d6c5c25390ee1027c8912e8c0a..0000000000000000000000000000000000000000
--- a/src/csharp/Grpc.Core/Credentials.cs
+++ /dev/null
@@ -1,138 +0,0 @@
-#region Copyright notice and license
-
-// 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.
-
-#endregion
-
-using System;
-using Grpc.Core.Internal;
-
-namespace Grpc.Core
-{
-    /// <summary>
-    /// Client-side credentials. Used for creation of a secure channel.
-    /// </summary>
-    public abstract class Credentials
-    {
-        static readonly Credentials InsecureInstance = new InsecureCredentialsImpl();
-
-        /// <summary>
-        /// Returns instance of credential that provides no security and 
-        /// will result in creating an unsecure channel with no encryption whatsoever.
-        /// </summary>
-        public static Credentials Insecure
-        {
-            get
-            {
-                return InsecureInstance;
-            }
-        }
-
-        /// <summary>
-        /// Creates native object for the credentials. May return null if insecure channel
-        /// should be created.
-        /// </summary>
-        /// <returns>The native credentials.</returns>
-        internal abstract CredentialsSafeHandle ToNativeCredentials();
-
-        private sealed class InsecureCredentialsImpl : Credentials
-        {
-            internal override CredentialsSafeHandle ToNativeCredentials()
-            {
-                return null;
-            }
-        }
-    }
-
-    /// <summary>
-    /// Client-side SSL credentials.
-    /// </summary>
-    public sealed class SslCredentials : Credentials
-    {
-        readonly string rootCertificates;
-        readonly KeyCertificatePair keyCertificatePair;
-
-        /// <summary>
-        /// Creates client-side SSL credentials loaded from
-        /// disk file pointed to by the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable.
-        /// If that fails, gets the roots certificates from a well known place on disk.
-        /// </summary>
-        public SslCredentials() : this(null, null)
-        {
-        }
-
-        /// <summary>
-        /// Creates client-side SSL credentials from
-        /// a string containing PEM encoded root certificates.
-        /// </summary>
-        public SslCredentials(string rootCertificates) : this(rootCertificates, null)
-        {
-        }
-            
-        /// <summary>
-        /// Creates client-side SSL credentials.
-        /// </summary>
-        /// <param name="rootCertificates">string containing PEM encoded server root certificates.</param>
-        /// <param name="keyCertificatePair">a key certificate pair.</param>
-        public SslCredentials(string rootCertificates, KeyCertificatePair keyCertificatePair)
-        {
-            this.rootCertificates = rootCertificates;
-            this.keyCertificatePair = keyCertificatePair;
-        }
-
-        /// <summary>
-        /// PEM encoding of the server root certificates.
-        /// </summary>
-        public string RootCertificates
-        {
-            get
-            {
-                return this.rootCertificates;
-            }
-        }
-
-        /// <summary>
-        /// Client side key and certificate pair.
-        /// If null, client will not use key and certificate pair.
-        /// </summary>
-        public KeyCertificatePair KeyCertificatePair
-        {
-            get
-            {
-                return this.keyCertificatePair;
-            }
-        }
-
-        internal override CredentialsSafeHandle ToNativeCredentials()
-        {
-            return CredentialsSafeHandle.CreateSslCredentials(rootCertificates, keyCertificatePair);
-        }
-    }
-}
diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index ad2af17bc75abcd8b81568f35ee8b35a6a127c5a..92d4e19eac25bc10c5d908be065230273a5e8f85 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -48,7 +48,9 @@
   <ItemGroup>
     <Compile Include="AsyncDuplexStreamingCall.cs" />
     <Compile Include="AsyncServerStreamingCall.cs" />
+    <Compile Include="CallCredentials.cs" />
     <Compile Include="IClientStreamWriter.cs" />
+    <Compile Include="Internal\NativeMetadataCredentialsPlugin.cs" />
     <Compile Include="Internal\INativeCall.cs" />
     <Compile Include="IServerStreamWriter.cs" />
     <Compile Include="IAsyncStreamWriter.cs" />
@@ -79,7 +81,7 @@
     <Compile Include="Utils\AsyncStreamExtensions.cs" />
     <Compile Include="Utils\BenchmarkUtil.cs" />
     <Compile Include="Internal\CredentialsSafeHandle.cs" />
-    <Compile Include="Credentials.cs" />
+    <Compile Include="ChannelCredentials.cs" />
     <Compile Include="Internal\ChannelArgsSafeHandle.cs" />
     <Compile Include="Internal\AsyncCompletion.cs" />
     <Compile Include="Internal\AsyncCallBase.cs" />
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index e3b00781c6216885cc9025ed26f3ef35d388e5b7..800462c85407a45a822a4fa5e00aa863255787b3 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -344,9 +344,13 @@ namespace Grpc.Core.Internal
 
             var parentCall = details.Options.PropagationToken != null ? details.Options.PropagationToken.ParentCall : CallSafeHandle.NullInstance;
 
-            return details.Channel.Handle.CreateCall(environment.CompletionRegistry,
-                parentCall, ContextPropagationToken.DefaultMask, cq,
-                details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value));
+            var credentials = details.Options.Credentials;
+            using (var nativeCredentials = credentials != null ? credentials.ToNativeCredentials() : null)
+            {
+                return details.Channel.Handle.CreateCall(environment.CompletionRegistry,
+                    parentCall, ContextPropagationToken.DefaultMask, cq,
+                    details.Method, details.Host, Timespec.FromDateTime(details.Options.Deadline.Value), nativeCredentials);
+            }
         }
 
         // Make sure that once cancellationToken for this call is cancelled, Cancel() will be called.
diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
index c3611a7761f5213e58c84a08a30a67b693a88f97..0be7a4dd3a1f3033321072360457ca7865baef95 100644
--- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
@@ -98,6 +98,9 @@ namespace Grpc.Core.Internal
         static extern GRPCCallError grpcsharp_call_send_initial_metadata(CallSafeHandle call,
             BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
 
+        [DllImport("grpc_csharp_ext.dll")]
+        static extern GRPCCallError grpcsharp_call_set_credentials(CallSafeHandle call, CredentialsSafeHandle credentials);
+
         [DllImport("grpc_csharp_ext.dll")]
         static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call);
 
@@ -113,6 +116,11 @@ namespace Grpc.Core.Internal
             this.completionRegistry = completionRegistry;
         }
 
+        public void SetCredentials(CredentialsSafeHandle credentials)
+        {
+            grpcsharp_call_set_credentials(this, credentials).CheckOk();
+        }
+
         public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
         {
             var ctx = BatchContextSafeHandle.Create();
diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
index 7a1c6e3dacd59885feaf6ce4020e3dc7ac30b168..d270d77526ffd44e69e6da66728bf614deef9852 100644
--- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
@@ -82,9 +82,13 @@ namespace Grpc.Core.Internal
             return grpcsharp_secure_channel_create(credentials, target, channelArgs);
         }
 
-        public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline)
+        public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline, CredentialsSafeHandle credentials)
         {
             var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline);
+            if (credentials != null)
+            {
+                result.SetCredentials(credentials);
+            }
             result.SetCompletionRegistry(registry);
             return result;
         }
diff --git a/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs
index feed33536246720f578c66223165c44752dc36b9..bab45108e02a060952e8b96dcc64cd807dd63779 100644
--- a/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CredentialsSafeHandle.cs
@@ -43,6 +43,9 @@ namespace Grpc.Core.Internal
         [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
         static extern CredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey);
 
+        [DllImport("grpc_csharp_ext.dll")]
+        static extern CredentialsSafeHandle grpcsharp_composite_credentials_create(CredentialsSafeHandle creds1, CredentialsSafeHandle creds2);
+
         [DllImport("grpc_csharp_ext.dll")]
         static extern void grpcsharp_credentials_release(IntPtr credentials);
 
@@ -69,6 +72,11 @@ namespace Grpc.Core.Internal
             }
         }
 
+        public static CredentialsSafeHandle CreateComposite(CredentialsSafeHandle creds1, CredentialsSafeHandle creds2)
+        {
+            return grpcsharp_composite_credentials_create(creds1, creds2);
+        }
+
         protected override bool ReleaseHandle()
         {
             grpcsharp_credentials_release(handle);
diff --git a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6662a73b17a61b077a984fea1dcfe9fa243a3f6b
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
@@ -0,0 +1,112 @@
+#region Copyright notice and license
+// 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.
+#endregion
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Grpc.Core.Logging;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Internal
+{
+    internal delegate void NativeMetadataInterceptor(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy);
+
+    internal class NativeMetadataCredentialsPlugin
+    {
+        const string GetMetadataExceptionMsg = "Exception occured in metadata credentials plugin.";
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<NativeMetadataCredentialsPlugin>();
+
+        [DllImport("grpc_csharp_ext.dll")]
+        static extern CredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor);
+        
+        [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
+        static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails);
+
+        AsyncAuthInterceptor interceptor;
+        GCHandle gcHandle;
+        NativeMetadataInterceptor nativeInterceptor;
+        CredentialsSafeHandle credentials;
+
+        public NativeMetadataCredentialsPlugin(AsyncAuthInterceptor interceptor)
+        {
+            this.interceptor = Preconditions.CheckNotNull(interceptor, "interceptor");
+            this.nativeInterceptor = NativeMetadataInterceptorHandler;
+
+            // Make sure the callback doesn't get garbage collected until it is destroyed.
+            this.gcHandle = GCHandle.Alloc(this.nativeInterceptor, GCHandleType.Normal);
+            this.credentials = grpcsharp_metadata_credentials_create_from_plugin(nativeInterceptor);
+        }
+
+        public CredentialsSafeHandle Credentials
+        {
+            get { return credentials; }
+        }
+
+        private void NativeMetadataInterceptorHandler(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy)
+        {
+            if (isDestroy)
+            {
+                gcHandle.Free();
+                return;
+            }
+
+            try
+            {
+                string serviceUrl = Marshal.PtrToStringAnsi(serviceUrlPtr);
+                StartGetMetadata(serviceUrl, callbackPtr, userDataPtr);
+            }
+            catch (Exception e)
+            {
+                grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, null, StatusCode.Unknown, GetMetadataExceptionMsg);
+                Logger.Error(e, GetMetadataExceptionMsg);
+            }
+        }
+
+        private async void StartGetMetadata(string serviceUrl, IntPtr callbackPtr, IntPtr userDataPtr)
+        {
+            try
+            {
+                var metadata = new Metadata();
+                await interceptor(serviceUrl, metadata);
+                using (var metadataArray = MetadataArraySafeHandle.Create(metadata))
+                {
+                    grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, metadataArray, StatusCode.OK, null);
+                }
+            }
+            catch (Exception e)
+            {
+                grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, null, StatusCode.Unknown, GetMetadataExceptionMsg);
+                Logger.Error(e, GetMetadataExceptionMsg);
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Examples.MathClient/MathClient.cs b/src/csharp/Grpc.Examples.MathClient/MathClient.cs
index 01e4a80babc119d666f69ce636c87f77286cd726..64e429ed5a32001bf89b10172ad73c40adb79ad0 100644
--- a/src/csharp/Grpc.Examples.MathClient/MathClient.cs
+++ b/src/csharp/Grpc.Examples.MathClient/MathClient.cs
@@ -39,7 +39,7 @@ namespace Math
     {
         public static void Main(string[] args)
         {
-            var channel = new Channel("127.0.0.1", 23456, Credentials.Insecure);
+            var channel = new Channel("127.0.0.1", 23456, ChannelCredentials.Insecure);
             Math.IMathClient client = new Math.MathClient(channel);
             MathExamples.DivExample(client);
 
diff --git a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
index e2975b5da93fd265899100aee3d5ec6d442119c2..290d42808e734cdae8aa4bcd07b1b1b29f6a69ea 100644
--- a/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
+++ b/src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
@@ -61,7 +61,7 @@ namespace Math.Tests
                 Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
             };
             server.Start();
-            channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
+            channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure);
             client = Math.NewClient(channel);
         }
 
diff --git a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
index 6c3a53bec0559168b54aab151394a855b6470fd1..d90f21c2e1cb30b7a3fe2e01297fb423cfa4a998 100644
--- a/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
+++ b/src/csharp/Grpc.HealthCheck.Tests/HealthClientServerTest.cs
@@ -63,7 +63,7 @@ namespace Grpc.HealthCheck.Tests
                 Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
             };
             server.Start();
-            channel = new Channel(Host, server.Ports.Single().BoundPort, Credentials.Insecure);
+            channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure);
 
             client = Grpc.Health.V1Alpha.Health.NewClient(channel);
         }
diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
index 2c38c9645c5f7804e73a69fb4576f3eab46a6703..8bc2082a1da0f736e833e9f442800449f2f44b12 100644
--- a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj
@@ -9,6 +9,7 @@
     <AssemblyName>Grpc.IntegrationTesting.Client</AssemblyName>
     <StartupObject>Grpc.IntegrationTesting.Client.Program</StartupObject>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <NuGetPackageImportStamp>6d22e68f</NuGetPackageImportStamp>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -38,7 +39,47 @@
     <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Threading.Tasks.Extensions">
+      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Net" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Net.Http.Extensions">
+      <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Net.Http.Primitives">
+      <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Net.Http.WebRequest" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Grpc.Core\Version.cs">
@@ -60,5 +101,13 @@
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />
+    <None Include="packages.config" />
   </ItemGroup>
+  <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
+  </Target>
 </Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.IntegrationTesting.Client/packages.config b/src/csharp/Grpc.IntegrationTesting.Client/packages.config
new file mode 100644
index 0000000000000000000000000000000000000000..7a02c95db9138a30a7f1bf2d432e95050e008d91
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting.Client/packages.config
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
+  <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
+  <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
+  <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
+  <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
+  <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
+  <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
+  <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
+</packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
index 949ad61375c4c0c15562cc8ab5a358e8c8c99e98..1eadbeb9206fb99dbf8f46df8b62f82e5079908e 100644
--- a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
+++ b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj
@@ -9,6 +9,7 @@
     <AssemblyName>Grpc.IntegrationTesting.Server</AssemblyName>
     <StartupObject>Grpc.IntegrationTesting.Server.Program</StartupObject>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <NuGetPackageImportStamp>d9ee8e52</NuGetPackageImportStamp>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -38,7 +39,47 @@
     <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Auth.PlatformServices, Version=1.9.3.19383, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Google.Apis.Auth.1.9.3\lib\net40\Google.Apis.Auth.PlatformServices.dll</HintPath>
+    </Reference>
+    <Reference Include="Google.Apis.Core, Version=1.9.3.19379, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Google.Apis.Core.1.9.3\lib\portable-net40+sl50+win+wpa81+wp80\Google.Apis.Core.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Threading.Tasks.Extensions">
+      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Net" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Net.Http.Extensions">
+      <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Net.Http.Primitives">
+      <HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Net.Http.WebRequest" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\Grpc.Core\Version.cs">
@@ -60,5 +101,13 @@
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />
+    <None Include="packages.config" />
   </ItemGroup>
+  <Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
+  </Target>
 </Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.IntegrationTesting.Server/packages.config b/src/csharp/Grpc.IntegrationTesting.Server/packages.config
new file mode 100644
index 0000000000000000000000000000000000000000..7a02c95db9138a30a7f1bf2d432e95050e008d91
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting.Server/packages.config
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
+  <package id="Google.Apis.Auth" version="1.9.3" targetFramework="net45" />
+  <package id="Google.Apis.Core" version="1.9.3" targetFramework="net45" />
+  <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
+  <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
+  <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
+  <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
+  <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
+</packages>
\ No newline at end of file
diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
index a0bcf431f7bee447b8428d51bd03be2c1014ee3f..f0a39acf753e7820a9e621f3bbe7c1c3c5af08fb 100644
--- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
+++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
@@ -96,6 +96,7 @@
     <Compile Include="Empty.cs" />
     <Compile Include="Messages.cs" />
     <Compile Include="InteropClientServerTest.cs" />
+    <Compile Include="MetadataCredentialsTest.cs" />
     <Compile Include="TestServiceImpl.cs" />
     <Compile Include="InteropServer.cs" />
     <Compile Include="InteropClient.cs" />
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
index 504d798b8936f7e3d54f75dcde1525a99d81077d..0df4ee35ba99335ecf1aba4db2bff391e257a4fe 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
@@ -33,11 +33,13 @@
 
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Text.RegularExpressions;
 using System.Threading;
 using System.Threading.Tasks;
 
 using CommandLine;
+using CommandLine.Text;
 using Google.Apis.Auth.OAuth2;
 using Google.Protobuf;
 using Grpc.Auth;
@@ -45,8 +47,6 @@ using Grpc.Core;
 using Grpc.Core.Utils;
 using Grpc.Testing;
 using NUnit.Framework;
-using CommandLine.Text;
-using System.IO;
 
 namespace Grpc.IntegrationTesting
 {
@@ -116,7 +116,7 @@ namespace Grpc.IntegrationTesting
 
         private async Task Run()
         {
-            var credentials = options.UseTls ? TestCredentials.CreateTestClientCredentials(options.UseTestCa) : Credentials.Insecure;
+            var credentials = await CreateCredentialsAsync();
             
             List<ChannelOption> channelOptions = null;
             if (!string.IsNullOrEmpty(options.ServerHostOverride))
@@ -132,6 +132,26 @@ namespace Grpc.IntegrationTesting
             await channel.ShutdownAsync();
         }
 
+        private async Task<ChannelCredentials> CreateCredentialsAsync()
+        {
+            var credentials = options.UseTls ? TestCredentials.CreateTestClientCredentials(options.UseTestCa) : ChannelCredentials.Insecure;
+
+            if (options.TestCase == "jwt_token_creds")
+            {
+                var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
+                Assert.IsTrue(googleCredential.IsCreateScopedRequired);
+                credentials = ChannelCredentials.Create(credentials, googleCredential.ToGrpcCredentials());
+            }
+
+            if (options.TestCase == "compute_engine_creds")
+            {
+                var googleCredential = await GoogleCredential.GetApplicationDefaultAsync();
+                Assert.IsFalse(googleCredential.IsCreateScopedRequired);
+                credentials = ChannelCredentials.Create(credentials, googleCredential.ToGrpcCredentials());
+            }
+            return credentials;
+        }
+
         private async Task RunTestCaseAsync(TestService.TestServiceClient client, ClientOptions options)
         {
             switch (options.TestCase)
@@ -155,10 +175,10 @@ namespace Grpc.IntegrationTesting
                     await RunEmptyStreamAsync(client);
                     break;
                 case "compute_engine_creds":
-                    await RunComputeEngineCredsAsync(client, options.DefaultServiceAccount, options.OAuthScope);
+                    RunComputeEngineCreds(client, options.DefaultServiceAccount, options.OAuthScope);
                     break;
                 case "jwt_token_creds":
-                    await RunJwtTokenCredsAsync(client, options.DefaultServiceAccount);
+                    RunJwtTokenCreds(client, options.DefaultServiceAccount);
                     break;
                 case "oauth2_auth_token":
                     await RunOAuth2AuthTokenAsync(client, options.DefaultServiceAccount, options.OAuthScope);
@@ -318,13 +338,10 @@ namespace Grpc.IntegrationTesting
             Console.WriteLine("Passed!");
         }
 
-        public static async Task RunComputeEngineCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope)
+        public static void RunComputeEngineCreds(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope)
         {
             Console.WriteLine("running compute_engine_creds");
-            var credential = await GoogleCredential.GetApplicationDefaultAsync();
-            Assert.IsFalse(credential.IsCreateScopedRequired);
-            client.HeaderInterceptor = AuthInterceptors.FromCredential(credential);
-            
+
             var request = new SimpleRequest
             {
                 ResponseType = PayloadType.COMPRESSABLE,
@@ -334,6 +351,7 @@ namespace Grpc.IntegrationTesting
                 FillOauthScope = true
             };
 
+            // not setting credentials here because they were set on channel already
             var response = client.UnaryCall(request);
 
             Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
@@ -344,13 +362,10 @@ namespace Grpc.IntegrationTesting
             Console.WriteLine("Passed!");
         }
 
-        public static async Task RunJwtTokenCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount)
+        public static void RunJwtTokenCreds(TestService.TestServiceClient client, string defaultServiceAccount)
         {
             Console.WriteLine("running jwt_token_creds");
-            var credential = await GoogleCredential.GetApplicationDefaultAsync();
-            Assert.IsTrue(credential.IsCreateScopedRequired);
-            client.HeaderInterceptor = AuthInterceptors.FromCredential(credential);
-
+           
             var request = new SimpleRequest
             {
                 ResponseType = PayloadType.COMPRESSABLE,
@@ -359,6 +374,7 @@ namespace Grpc.IntegrationTesting
                 FillUsername = true,
             };
 
+            // not setting credentials here because they were set on channel already
             var response = client.UnaryCall(request);
 
             Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
@@ -373,15 +389,14 @@ namespace Grpc.IntegrationTesting
             ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope });
             string oauth2Token = await credential.GetAccessTokenForRequestAsync();
 
-            client.HeaderInterceptor = AuthInterceptors.FromAccessToken(oauth2Token);
-
+            var credentials = GrpcCredentials.FromAccessToken(oauth2Token);
             var request = new SimpleRequest
             {
                 FillUsername = true,
                 FillOauthScope = true
             };
 
-            var response = client.UnaryCall(request);
+            var response = client.UnaryCall(request, new CallOptions(credentials: credentials));
 
             Assert.False(string.IsNullOrEmpty(response.OauthScope));
             Assert.True(oauthScope.Contains(response.OauthScope));
@@ -392,18 +407,15 @@ namespace Grpc.IntegrationTesting
         public static async Task RunPerRpcCredsAsync(TestService.TestServiceClient client, string defaultServiceAccount, string oauthScope)
         {
             Console.WriteLine("running per_rpc_creds");
-            ITokenAccess credential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope });
-            string accessToken = await credential.GetAccessTokenForRequestAsync();
-            var headerInterceptor = AuthInterceptors.FromAccessToken(accessToken);
+            ITokenAccess googleCredential = (await GoogleCredential.GetApplicationDefaultAsync()).CreateScoped(new[] { oauthScope });
 
+            var credentials = GrpcCredentials.Create(googleCredential);
             var request = new SimpleRequest
             {
                 FillUsername = true,
             };
 
-            var headers = new Metadata();
-            headerInterceptor(null, "", headers);
-            var response = client.UnaryCall(request, headers: headers);
+            var response = client.UnaryCall(request, new CallOptions(credentials: credentials));
 
             Assert.AreEqual(defaultServiceAccount, response.Username);
             Console.WriteLine("Passed!");
diff --git a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5325b2fa148a66cee882e8398909f404af1fe00f
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
@@ -0,0 +1,97 @@
+#region Copyright notice and license
+
+// 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.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+using Grpc.Core.Utils;
+using Grpc.Testing;
+using NUnit.Framework;
+
+namespace Grpc.IntegrationTesting
+{
+    public class MetadataCredentialsTest
+    {
+        const string Host = "localhost";
+        Server server;
+        Channel channel;
+        TestService.ITestServiceClient client;
+
+        [TestFixtureSetUp]
+        public void Init()
+        {
+            var serverCredentials = new SslServerCredentials(new[] { new KeyCertificatePair(File.ReadAllText(TestCredentials.ServerCertChainPath), File.ReadAllText(TestCredentials.ServerPrivateKeyPath)) });
+            server = new Server
+            {
+                Services = { TestService.BindService(new TestServiceImpl()) },
+                Ports = { { Host, ServerPort.PickUnused, serverCredentials } }
+            };
+            server.Start();
+
+            var options = new List<ChannelOption>
+            {
+                new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
+            };
+
+            var asyncAuthInterceptor = new AsyncAuthInterceptor(async (authUri, metadata) =>
+            {
+                await Task.Delay(100);  // make sure the operation is asynchronous.
+                metadata.Add("authorization", "SECRET_TOKEN");
+            });
+
+            var clientCredentials = ChannelCredentials.Create(
+                new SslCredentials(File.ReadAllText(TestCredentials.ClientCertAuthorityPath)),
+                new MetadataCredentials(asyncAuthInterceptor));
+            channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options);
+            client = TestService.NewClient(channel);
+        }
+
+        [TestFixtureTearDown]
+        public void Cleanup()
+        {
+            channel.ShutdownAsync().Wait();
+            server.ShutdownAsync().Wait();
+        }
+
+        [Test]
+        public void MetadataCredentials()
+        {
+            var response = client.UnaryCall(new SimpleRequest { ResponseSize = 10 });
+            Assert.AreEqual(10, response.Payload.Body.Length);
+        }
+    }
+}
diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c
index 51e0728fb9dd17b7a7c6b88ecb7ed27a2836a620..679ca43d74913c570a1b1c58988aae75af092b04 100644
--- a/src/csharp/ext/grpc_csharp_ext.c
+++ b/src/csharp/ext/grpc_csharp_ext.c
@@ -68,7 +68,7 @@ grpc_byte_buffer *string_to_byte_buffer(const char *buffer, size_t len) {
 /*
  * Helper to maintain lifetime of batch op inputs and store batch op outputs.
  */
-typedef struct gprcsharp_batch_context {
+typedef struct grpcsharp_batch_context {
   grpc_metadata_array send_initial_metadata;
   grpc_byte_buffer *send_message;
   struct {
@@ -665,16 +665,16 @@ grpcsharp_call_start_duplex_streaming(grpc_call *call,
 }
 
 GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_recv_initial_metadata(
-	grpc_call *call, grpcsharp_batch_context *ctx) {
-	/* TODO: don't use magic number */
-	grpc_op ops[1];
-	ops[0].op = GRPC_OP_RECV_INITIAL_METADATA;
-	ops[0].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
-	ops[0].flags = 0;
-	ops[0].reserved = NULL;
+  grpc_call *call, grpcsharp_batch_context *ctx) {
+  /* TODO: don't use magic number */
+  grpc_op ops[1];
+  ops[0].op = GRPC_OP_RECV_INITIAL_METADATA;
+  ops[0].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
+  ops[0].flags = 0;
+  ops[0].reserved = NULL;
 
-	return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
-		NULL);
+  return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx,
+    NULL);
 }
 
 GPR_EXPORT grpc_call_error GPR_CALLTYPE
@@ -785,6 +785,11 @@ grpcsharp_call_send_initial_metadata(grpc_call *call,
                                NULL);
 }
 
+GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_set_credentials(grpc_call *call,
+                                                            grpc_credentials *creds) {
+	return grpc_call_set_credentials(call, creds);
+}
+
 /* Server */
 
 GPR_EXPORT grpc_server *GPR_CALLTYPE
@@ -892,6 +897,47 @@ grpcsharp_server_add_secure_http2_port(grpc_server *server, const char *addr,
   return grpc_server_add_secure_http2_port(server, addr, creds);
 }
 
+GPR_EXPORT grpc_credentials *GPR_CALLTYPE grpcsharp_composite_credentials_create(
+  grpc_credentials *creds1,
+  grpc_credentials *creds2) {
+  return grpc_composite_credentials_create(creds1, creds2, NULL);
+}
+
+/* Metadata credentials plugin */
+
+GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin(
+    grpc_credentials_plugin_metadata_cb cb,
+    void *user_data, grpc_metadata_array *metadata,
+  grpc_status_code status, const char *error_details) {
+  cb(user_data, metadata->metadata, metadata->count, status, error_details);
+}
+
+typedef void(GPR_CALLTYPE *grpcsharp_metadata_interceptor_func)(
+  void *state, const char *service_url, grpc_credentials_plugin_metadata_cb cb,
+  void *user_data, gpr_int32 is_destroy);
+
+static void grpcsharp_get_metadata_handler(void *state, const char *service_url,
+  grpc_credentials_plugin_metadata_cb cb, void *user_data) {
+  grpcsharp_metadata_interceptor_func interceptor =
+      (grpcsharp_metadata_interceptor_func)(gpr_intptr)state;
+  interceptor(state, service_url, cb, user_data, 0);
+}
+
+static void grpcsharp_metadata_credentials_destroy_handler(void *state) {
+  grpcsharp_metadata_interceptor_func interceptor =
+      (grpcsharp_metadata_interceptor_func)(gpr_intptr)state;
+  interceptor(state, NULL, NULL, NULL, 1);
+}
+
+GPR_EXPORT grpc_credentials *GPR_CALLTYPE grpcsharp_metadata_credentials_create_from_plugin(
+  grpcsharp_metadata_interceptor_func metadata_interceptor) {
+  grpc_metadata_credentials_plugin plugin;
+  plugin.get_metadata = grpcsharp_get_metadata_handler;
+  plugin.destroy = grpcsharp_metadata_credentials_destroy_handler;
+  plugin.state = (void*)(gpr_intptr)metadata_interceptor;
+  return grpc_metadata_credentials_create_from_plugin(plugin, NULL);
+}
+
 /* Logging */
 
 typedef void(GPR_CALLTYPE *grpcsharp_log_func)(const char *file, gpr_int32 line,